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