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