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