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