Reference documentation for deal.II version GIT e22cb6a53e 2023-09-21 14:00:02+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
table_handler.cc
Go to the documentation of this file.
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 1999 - 2022 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE.md at
12 // the top level directory of deal.II.
13 //
14 // ---------------------------------------------------------------------
15 
17 
18 #include <boost/io/ios_state.hpp>
19 
20 #include <algorithm>
21 #include <iomanip>
22 #include <iostream>
23 #include <sstream>
24 
25 
27 
28 
29 /*---------------------------------------------------------------------*/
30 
31 // inline and template functions
32 namespace internal
33 {
34  double
36  {
37  // we don't quite know the data type in 'value', but
38  // it must be one of the ones in the type list of the
39  // std::variant. Go through this list and return
40  // the value if this happens to be a number
41  //
42  // first try with int
43  try
44  {
45  return std::get<int>(value);
46  }
47  catch (...)
48  {}
49 
50 
51  // ... then with unsigned int...
52  try
53  {
54  return std::get<unsigned int>(value);
55  }
56  catch (...)
57  {}
58 
59  // ... then with std::uint64_t...
60  try
61  {
62  return std::get<std::uint64_t>(value);
63  }
64  catch (...)
65  {}
66 
67  // ...and finally with double precision:
68  try
69  {
70  return std::get<double>(value);
71  }
72  catch (...)
73  {
74  Assert(false,
75  ExcMessage("The number stored by this element of the "
76  "table is not a number."))
77  }
78 
79  return 0;
80  }
81 
82  void
83  TableEntry::cache_string(bool scientific, unsigned int precision) const
84  {
85  std::ostringstream ss;
86 
87  ss << std::setprecision(precision);
88 
89  if (scientific)
90  ss.setf(std::ios::scientific, std::ios::floatfield);
91  else
92  ss.setf(std::ios::fixed, std::ios::floatfield);
93 
94  std::visit([&ss](auto &v) { ss << v; }, value);
95 
96  cached_value = ss.str();
97  if (cached_value.empty())
98  cached_value = "\"\"";
99  }
100 
101  const std::string &
103  {
104  return cached_value;
105  }
106 
107  TableEntry
109  {
110  TableEntry new_entry = *this;
111  // Let std::visit figure out which data type is actually stored,
112  // and then set the object so stored to a default-constructed
113  // one.
114  std::visit([](
115  auto &arg) { arg = std::remove_reference_t<decltype(arg)>(); },
116  new_entry.value);
117 
118  return new_entry;
119  }
120 
121 
122 } // namespace internal
123 
124 /* ------------------------------------------------ */
125 
126 TableHandler::Column::Column(const std::string &tex_caption)
127  : tex_caption(tex_caption)
128  , tex_format("c")
129  , precision(4)
130  , scientific(false)
131  , flag(0)
132  , max_length(0)
133 {}
134 
135 
136 
138  : tex_caption()
139  , tex_format("c")
140  , precision(4)
141  , scientific(false)
142  , flag(0)
143  , max_length(0)
144 {}
145 
146 
147 
148 void
150 {
151  // we should never have a column that is completely
152  // empty and that needs to be padded
153  Assert(entries.size() > 0, ExcInternalError());
154 
155  // add as many elements as necessary
156  while (entries.size() < size)
157  {
158  entries.push_back(entries.back().get_default_constructed_copy());
159  internal::TableEntry &entry = entries.back();
160  entry.cache_string(scientific, precision);
161  max_length =
162  std::max(max_length,
163  static_cast<unsigned int>(entry.get_cached_string().size()));
164  }
165 }
166 
167 
168 void
170 {
171  max_length = 0;
172 
173  for (const auto &entry : entries)
174  {
175  entry.cache_string(this->scientific, this->precision);
176  max_length =
177  std::max(max_length,
178  static_cast<unsigned int>(entry.get_cached_string().size()));
179  }
180 }
181 
182 
183 /*---------------------------------------------------------------------*/
184 
185 
187  : auto_fill_mode(false)
188 {}
189 
190 
191 
192 void
193 TableHandler::declare_column(const std::string &key)
194 {
195  // see if the column already exists; insert it if not
196  Assert(columns.find(key) == columns.end(),
197  ExcMessage("You are trying to declare a column with key <" + key +
198  "> but such a column already exists."));
199 
200  columns.insert(std::make_pair(key, Column(key)));
201  column_order.push_back(key);
202 }
203 
204 
205 
206 void
208 {
209  // figure out the longest current column
210  unsigned int max_col_length = 0;
211  for (const auto &column : columns)
212  max_col_length =
213  std::max(max_col_length,
214  static_cast<unsigned int>(column.second.entries.size()));
215 
216 
217  // then pad all columns to that length with empty strings
218  for (auto &column : columns)
219  while (column.second.entries.size() < max_col_length)
220  {
221  column.second.entries.emplace_back("");
222  internal::TableEntry &entry = column.second.entries.back();
223  entry.cache_string(column.second.scientific, column.second.precision);
224  column.second.max_length =
225  std::max(column.second.max_length,
226  static_cast<unsigned int>(entry.get_cached_string().size()));
227  }
228 }
229 
230 
231 
232 void
234 {
235  auto_fill_mode = state;
236 }
237 
238 
239 void
241  const std::string &superkey)
242 {
243  Assert(columns.count(key), ExcColumnNotExistent(key));
244 
245  if (supercolumns.count(superkey) == 0u)
246  {
247  std::pair<std::string, std::vector<std::string>> new_column(
248  superkey, std::vector<std::string>());
249  supercolumns.insert(new_column);
250  // replace key in column_order
251  // by superkey
252  for (auto &column : column_order)
253  if (column == key)
254  {
255  column = superkey;
256  break;
257  }
258  }
259  else
260  {
261  // remove key from column_order
262  const auto order_iter =
263  std::find(column_order.begin(), column_order.end(), key);
264  if (order_iter != column_order.end())
265  column_order.erase(order_iter);
266  }
267 
268  if (supercolumns.count(superkey) != 0u)
269  {
270  supercolumns[superkey].push_back(key);
271  // By default set the
272  // tex_supercaption to superkey
273  std::pair<std::string, std::string> new_tex_supercaption(superkey,
274  superkey);
275  tex_supercaptions.insert(new_tex_supercaption);
276  }
277  else
278  Assert(false, ExcInternalError());
279 }
280 
281 
282 
283 void
284 TableHandler::set_column_order(const std::vector<std::string> &new_order)
285 {
286  for (const auto &new_column : new_order)
287  {
288  (void)new_column;
289  Assert(supercolumns.count(new_column) || columns.count(new_column),
291  }
292 
293  column_order = new_order;
294 }
295 
296 
297 void
298 TableHandler::set_tex_caption(const std::string &key,
299  const std::string &tex_caption)
300 {
301  Assert(columns.count(key), ExcColumnNotExistent(key));
302  columns[key].tex_caption = tex_caption;
303 }
304 
305 
306 
307 void
308 TableHandler::set_tex_table_caption(const std::string &table_caption)
309 {
310  tex_table_caption = table_caption;
311 }
312 
313 
314 
315 void
316 TableHandler::set_tex_table_label(const std::string &table_label)
317 {
318  tex_table_label = table_label;
319 }
320 
321 
322 
323 void
324 TableHandler::set_tex_supercaption(const std::string &superkey,
325  const std::string &tex_supercaption)
326 {
327  Assert(supercolumns.count(superkey), ExcSuperColumnNotExistent(superkey));
328  Assert(tex_supercaptions.count(superkey), ExcInternalError());
329  tex_supercaptions[superkey] = tex_supercaption;
330 }
331 
332 
333 
334 void
335 TableHandler::set_tex_format(const std::string &key,
336  const std::string &tex_format)
337 {
338  Assert(columns.count(key), ExcColumnNotExistent(key));
339  Assert(tex_format == "l" || tex_format == "c" || tex_format == "r",
340  ExcUndefinedTexFormat(tex_format));
341  columns[key].tex_format = tex_format;
342 }
343 
344 
345 
346 void
347 TableHandler::set_precision(const std::string &key,
348  const unsigned int precision)
349 {
350  Assert(columns.count(key), ExcColumnNotExistent(key));
351  if (columns[key].precision != precision)
352  {
353  columns[key].precision = precision;
354  columns[key].invalidate_cache();
355  }
356 }
357 
358 
359 void
360 TableHandler::set_scientific(const std::string &key, const bool scientific)
361 {
362  Assert(columns.count(key), ExcColumnNotExistent(key));
363  if (columns[key].scientific != scientific)
364  {
365  columns[key].scientific = scientific;
366  columns[key].invalidate_cache();
367  }
368 }
369 
370 
371 void
372 TableHandler::write_text(std::ostream &out, const TextOutputFormat format) const
373 {
374  AssertThrow(out.fail() == false, ExcIO());
375  boost::io::ios_flags_saver restore_flags(out);
376 
377  // first pad the table from below if necessary
378  if (auto_fill_mode == true)
379  {
380  unsigned int max_rows = 0;
381  for (std::map<std::string, Column>::const_iterator p = columns.begin();
382  p != columns.end();
383  ++p)
384  max_rows = std::max<unsigned int>(max_rows, p->second.entries.size());
385 
386  for (auto &column : columns)
387  column.second.pad_column_below(max_rows);
388  }
389 
390  std::vector<std::string> sel_columns;
391  get_selected_columns(sel_columns);
392 
393  const unsigned int nrows = n_rows();
394  const unsigned int n_cols = sel_columns.size();
395 
396  // cache the columns and compute the widths of each column for alignment
397  std::vector<const Column *> cols;
398  std::vector<unsigned int> column_widths(n_cols, 0);
399  for (unsigned int j = 0; j < n_cols; ++j)
400  {
401  std::string key = sel_columns[j];
402  const std::map<std::string, Column>::const_iterator col_iter =
403  columns.find(key);
404  Assert(col_iter != columns.end(), ExcInternalError());
405  cols.push_back(&(col_iter->second));
406 
407  column_widths[j] = col_iter->second.max_length;
408  }
409 
410  switch (format)
411  {
412  case org_mode_table:
413  {
414  // write the captions
415  out << "| " << std::left;
416  for (unsigned int j = 0; j < n_cols; ++j)
417  {
418  const std::string &key = sel_columns[j];
419  column_widths[j] =
420  std::max(column_widths[j],
421  static_cast<unsigned int>(key.size()));
422  out << std::setw(column_widths[j]);
423  out << key << " | ";
424  }
425  out << '\n';
426 
427  // write the body
428  for (unsigned int i = 0; i < nrows; ++i)
429  {
430  out << "| ";
431  for (unsigned int j = 0; j < n_cols; ++j)
432  {
433  const Column &column = *(cols[j]);
434 
435  out << std::setw(column_widths[j]);
436  out << column.entries[i].get_cached_string();
437  out << " | ";
438  }
439  out << '\n';
440  }
441 
442  out << std::flush;
443  return;
444  }
445 
447  {
448  // write the captions
449  for (unsigned int j = 0; j < n_cols; ++j)
450  {
451  const std::string &key = sel_columns[j];
452  out << "# " << j + 1 << ": " << key << '\n';
453  }
454 
455  // write the body
456  for (unsigned int i = 0; i < nrows; ++i)
457  {
458  for (unsigned int j = 0; j < n_cols; ++j)
459  {
460  const Column &column = *(cols[j]);
461 
462  out << column.entries[i].get_cached_string();
463  out << ' ';
464  }
465  out << '\n';
466  }
467 
468  out << std::flush;
469  return;
470  }
471 
473  {
474  // writing the captions for table_with_separate_column_description
475  // means that we ignore supercolumns and output the column
476  // header for each column. enumerate columns starting with 1
477  for (unsigned int j = 0; j < n_cols; ++j)
478  {
479  std::string key = sel_columns[j];
480  out << "# " << j + 1 << ": " << key << '\n';
481  }
482  break;
483  }
484 
485  case table_with_headers:
486  {
487  // This format output supercolumn headers and aligns them centered
488  // over all the columns that belong to it.
489  for (const auto &key : column_order)
490  {
491  unsigned int width = 0;
492  {
493  // compute the width of this column or supercolumn
494  const std::map<std::string,
495  std::vector<std::string>>::const_iterator
496  super_iter = supercolumns.find(key);
497  if (super_iter != supercolumns.end())
498  {
499  const unsigned int n_subcolumns = super_iter->second.size();
500  for (unsigned int k = 0; k < n_subcolumns; ++k)
501  {
502  const std::map<std::string, Column>::const_iterator
503  col_iter = columns.find(super_iter->second[k]);
504  Assert(col_iter != columns.end(), ExcInternalError());
505 
506  width += col_iter->second.max_length;
507  }
508  width += n_subcolumns - 1; // separators between subcolumns
509  }
510  else
511  {
512  const std::map<std::string, Column>::const_iterator
513  col_iter = columns.find(key);
514 
515  width = col_iter->second.max_length;
516  }
517  }
518 
519  // header is longer than the column(s) under it
520  if (width < key.size())
521  {
522  // make the column or the last column in this
523  // supercolumn wide enough
524  std::string colname;
525 
526  const std::map<std::string,
527  std::vector<std::string>>::const_iterator
528  super_iter = supercolumns.find(key);
529  if (super_iter != supercolumns.end())
530  colname = super_iter->second.back();
531  else
532  colname = key;
533 
534  // find column and change output width
535  for (unsigned int i = 0; i < n_cols; ++i)
536  {
537  if (sel_columns[i] == colname)
538  {
539  column_widths[i] += key.size() - width;
540  break;
541  }
542  }
543 
544  width = key.size();
545  }
546 
547  // now write key. try to center it somehow
548  const unsigned int front_padding = (width - key.size()) / 2,
549  rear_padding =
550  (width - key.size()) - front_padding;
551  for (unsigned int i = 0; i < front_padding; ++i)
552  out << ' ';
553  out << key;
554  for (unsigned int i = 0; i < rear_padding; ++i)
555  out << ' ';
556 
557  out << ' ';
558  }
559  out << '\n';
560  break;
561  }
562 
563  default:
564  Assert(false, ExcInternalError());
565  }
566 
567 
568  // finally output the data itself for
569  // table_with_headers or table_with_separate_column_description:
570  for (unsigned int i = 0; i < nrows; ++i)
571  {
572  for (unsigned int j = 0; j < n_cols; ++j)
573  {
574  const Column &column = *(cols[j]);
575  out << std::setw(column_widths[j]);
576  out << column.entries[i].get_cached_string();
577 
578  // pad after this column
579  out << ' ';
580  }
581  out << '\n';
582  }
583  out << std::flush;
584 }
585 
586 
587 void
588 TableHandler::write_tex(std::ostream &out, const bool with_header) const
589 {
590  // TODO[TH]: update code similar to
591  // write_text() to use the cache
592  AssertThrow(out.fail() == false, ExcIO());
593  if (with_header)
594  out << "\\documentclass[10pt]{report}" << '\n'
595  << "\\usepackage{float}" << '\n'
596  << '\n'
597  << '\n'
598  << "\\begin{document}" << '\n';
599 
600  out << "\\begin{table}[H]" << '\n'
601  << "\\begin{center}" << '\n'
602  << "\\begin{tabular}{|";
603 
604  // first pad the table from below if necessary
605  if (auto_fill_mode == true)
606  {
607  unsigned int max_rows = 0;
608  for (std::map<std::string, Column>::const_iterator p = columns.begin();
609  p != columns.end();
610  ++p)
611  max_rows = std::max<unsigned int>(max_rows, p->second.entries.size());
612 
613  for (auto &column : columns)
614  column.second.pad_column_below(max_rows);
615  }
616 
617  std::vector<std::string> sel_columns;
618  get_selected_columns(sel_columns);
619 
620  // write the column formats
621  for (const auto &key : column_order)
622  {
623  // avoid `supercolumns[key]'
624  const std::map<std::string, std::vector<std::string>>::const_iterator
625  super_iter = supercolumns.find(key);
626 
627  if (super_iter != supercolumns.end())
628  {
629  const unsigned int n_subcolumns = super_iter->second.size();
630  for (unsigned int k = 0; k < n_subcolumns; ++k)
631  {
632  // avoid `columns[supercolumns[key]]'
633  const std::map<std::string, Column>::const_iterator col_iter =
634  columns.find(super_iter->second[k]);
635  Assert(col_iter != columns.end(), ExcInternalError());
636 
637  out << col_iter->second.tex_format << "|";
638  }
639  }
640  else
641  {
642  // avoid `columns[key]';
643  const std::map<std::string, Column>::const_iterator col_iter =
644  columns.find(key);
645  Assert(col_iter != columns.end(), ExcInternalError());
646  out << col_iter->second.tex_format << "|";
647  }
648  }
649  out << "} \\hline" << '\n';
650 
651  // write the caption line of the table
652 
653  for (unsigned int j = 0; j < column_order.size(); ++j)
654  {
655  std::string key = column_order[j];
656  const std::map<std::string, std::vector<std::string>>::const_iterator
657  super_iter = supercolumns.find(key);
658 
659  if (super_iter != supercolumns.end())
660  {
661  const unsigned int n_subcolumns = super_iter->second.size();
662  // avoid use of `tex_supercaptions[key]'
663  std::map<std::string, std::string>::const_iterator
664  tex_super_cap_iter = tex_supercaptions.find(key);
665  out << '\n'
666  << "\\multicolumn{" << n_subcolumns << "}{|c|}{"
667  << tex_super_cap_iter->second << "}";
668  }
669  else
670  {
671  // col_iter->second=columns[col];
672  const std::map<std::string, Column>::const_iterator col_iter =
673  columns.find(key);
674  Assert(col_iter != columns.end(), ExcInternalError());
675  out << col_iter->second.tex_caption;
676  }
677  if (j < column_order.size() - 1)
678  out << " & ";
679  }
680  out << "\\\\ \\hline" << '\n';
681 
682  // write the n rows
683  const unsigned int nrows = n_rows();
684  for (unsigned int i = 0; i < nrows; ++i)
685  {
686  const unsigned int n_cols = sel_columns.size();
687 
688  for (unsigned int j = 0; j < n_cols; ++j)
689  {
690  std::string key = sel_columns[j];
691  // avoid `column[key]'
692  const std::map<std::string, Column>::const_iterator col_iter =
693  columns.find(key);
694  Assert(col_iter != columns.end(), ExcInternalError());
695 
696  const Column &column = col_iter->second;
697 
698  out << std::setprecision(column.precision);
699 
700  if (col_iter->second.scientific)
701  out.setf(std::ios::scientific, std::ios::floatfield);
702  else
703  out.setf(std::ios::fixed, std::ios::floatfield);
704 
705  std::visit([&out](auto &v) { out << v; }, column.entries[i].value);
706 
707  if (j < n_cols - 1)
708  out << " & ";
709  }
710  out << "\\\\ \\hline" << '\n';
711  }
712 
713  out << "\\end{tabular}" << '\n' << "\\end{center}" << '\n';
714  if (!tex_table_caption.empty())
715  out << "\\caption{" << tex_table_caption << "}" << '\n';
716  if (!tex_table_label.empty())
717  out << "\\label{" << tex_table_label << "}" << '\n';
718  out << "\\end{table}" << '\n';
719  if (with_header)
720  out << "\\end{document}" << '\n';
721 
722  // Now flush all of the data we've put into the stream to make it
723  // sure it really gets written.
724  out << std::flush;
725 }
726 
727 
728 
729 void
731 {
732  columns.clear();
733  supercolumns.clear();
734  column_order.clear();
735  tex_supercaptions.clear();
736 
737  tex_table_label.clear();
738  tex_table_caption.clear();
739 }
740 
741 
742 unsigned int
744 {
745  if (columns.empty())
746  return 0;
747 
748  std::map<std::string, Column>::const_iterator col_iter = columns.begin();
749  unsigned int n = col_iter->second.entries.size();
750 
751 #ifdef DEBUG
752  std::string first_name = col_iter->first;
753  for (++col_iter; col_iter != columns.end(); ++col_iter)
754  Assert(col_iter->second.entries.size() == n,
756  col_iter->first, col_iter->second.entries.size(), first_name, n));
757 #endif
758 
759  return n;
760 }
761 
762 
763 void
764 TableHandler::get_selected_columns(std::vector<std::string> &sel_columns) const
765 {
766  sel_columns.clear();
767 
768  for (const auto &key : column_order)
769  {
770  const std::map<std::string, std::vector<std::string>>::const_iterator
771  super_iter = supercolumns.find(key);
772 
773  if (super_iter != supercolumns.end())
774  {
775  // i.e. key is a supercolumn key
776  const unsigned int n_subcolumns = super_iter->second.size();
777  for (unsigned int k = 0; k < n_subcolumns; ++k)
778  {
779  const std::string subkey = super_iter->second[k];
780  Assert(columns.count(subkey), ExcInternalError());
781  sel_columns.push_back(subkey);
782  }
783  }
784  else
785  {
786  Assert(columns.count(key), ExcInternalError());
787  // i.e. key is a column key
788  sel_columns.push_back(key);
789  }
790  }
791 }
792 
793 
794 void
796 {
797  // Figure out what is the correct (max) length of the columns
798  // so that we "shave" one off.
800  for (const auto &column : columns)
801  n = std::max(n, column.second.entries.size());
802 
803  // shave the top most element
804  if (n != 0)
805  for (auto &column : columns)
806  if (column.second.entries.size() == n)
807  column.second.entries.pop_back();
808 }
809 
810 
void set_tex_format(const std::string &key, const std::string &format="c")
void start_new_row()
void set_tex_table_caption(const std::string &table_caption)
void set_tex_supercaption(const std::string &superkey, const std::string &tex_supercaption)
void declare_column(const std::string &key)
void write_text(std::ostream &out, const TextOutputFormat format=table_with_headers) const
void set_auto_fill_mode(const bool state)
unsigned int n_rows() const
void get_selected_columns(std::vector< std::string > &sel_columns) const
std::map< std::string, std::vector< std::string > > supercolumns
@ simple_table_with_separate_column_description
@ table_with_separate_column_description
std::map< std::string, std::string > tex_supercaptions
std::string tex_table_label
std::string tex_table_caption
void write_tex(std::ostream &file, const bool with_header=true) const
void add_column_to_supercolumn(const std::string &key, const std::string &superkey)
void set_column_order(const std::vector< std::string > &new_order)
void set_tex_caption(const std::string &key, const std::string &tex_caption)
void clear_current_row()
void set_scientific(const std::string &key, const bool scientific)
std::vector< std::string > column_order
void set_tex_table_label(const std::string &table_label)
void set_precision(const std::string &key, const unsigned int precision)
std::map< std::string, Column > columns
#define DEAL_II_NAMESPACE_OPEN
Definition: config.h:477
#define DEAL_II_NAMESPACE_CLOSE
Definition: config.h:478
static ::ExceptionBase & ExcColumnNotExistent(std::string arg1)
static ::ExceptionBase & ExcUndefinedTexFormat(std::string arg1)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcColumnOrSuperColumnNotExistent(std::string arg1)
static ::ExceptionBase & ExcSuperColumnNotExistent(std::string arg1)
static ::ExceptionBase & ExcWrongNumberOfDataEntries(std::string arg1, int arg2, std::string arg3, int arg4)
#define Assert(cond, exc)
Definition: exceptions.h:1616
static ::ExceptionBase & ExcIO()
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
Definition: exceptions.h:1705
types::global_dof_index size_type
Definition: cuda_kernels.h:45
void pad_column_below(const unsigned int length)
unsigned int precision
std::vector< internal::TableEntry > entries
TableEntry get_default_constructed_copy() const
std::string cached_value
const std::string & get_cached_string() const
double get_numeric_value() const
void cache_string(bool scientific, unsigned int precision) const