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