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