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