From: bangerth Date: Tue, 18 Oct 2011 11:19:20 +0000 (+0000) Subject: Fill partially filled columns with padding. X-Git-Url: https://gitweb.dealii.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=12b265e904986e278ab90a7778ff9a8e8b50071a;p=dealii-svn.git Fill partially filled columns with padding. git-svn-id: https://svn.dealii.org/trunk@24625 0785d39b-7218-0410-832d-ea1e28bc413d --- diff --git a/deal.II/doc/news/changes.h b/deal.II/doc/news/changes.h index f09d237f56..e1c7bb66ef 100644 --- a/deal.II/doc/news/changes.h +++ b/deal.II/doc/news/changes.h @@ -44,6 +44,11 @@ inconvenience this causes.

Specific improvements

    +
  1. New: The TableHandler class now pads columns that have only been +partially filled. See the documentation of the class for a description. +
    +(Wolfgang Bangerth, 2011/10/18) +
  2. Fixed: In TableHandler::print_text, it can happen that the function wants to print an empty string as the element of the table to be printed. This can confuse machine readers of this table, for example for visualization, diff --git a/deal.II/include/deal.II/base/table_handler.h b/deal.II/include/deal.II/base/table_handler.h index 35e9e5b3c4..168ffb3a2a 100644 --- a/deal.II/include/deal.II/base/table_handler.h +++ b/deal.II/include/deal.II/base/table_handler.h @@ -38,6 +38,7 @@ DEAL_II_NAMESPACE_OPEN +class TableHandler; namespace internal { @@ -45,23 +46,20 @@ namespace internal * A TableEntry stores the value of a table entry. * It can either be of type int, unsigned int, double or std::string. * In essence, this structure is the same as - * boost::variant + * boost::variant@ * but we wrap this object in a structure for which we can write * a function that can serialize it. This is also why the function * is not in fact of type boost::any. */ struct TableEntry { + public: /** - * Abbreviation for the data type stored by this object. - */ - typedef boost::variant value_type; - - /** - * Stored value. + * Constructor. Initialize this table element with the value t. */ - value_type value; - + template + TableEntry (const T &t); + /** * Return the value stored by this object. The template type * T must be one of int,unsigned int,double,std::string @@ -96,6 +94,19 @@ namespace internal void load (Archive & ar, const unsigned int version); BOOST_SERIALIZATION_SPLIT_MEMBER() + + private: + /** + * Abbreviation for the data type stored by this object. + */ + typedef boost::variant value_type; + + /** + * Stored value. + */ + value_type value; + + friend class dealii::TableHandler; }; } @@ -161,6 +172,48 @@ namespace internal * out_file.close(); * @endcode * + * + *

    Dealing with sparse data

    + * + * When generating output, TableHandler expects that all columns have the exact + * same number of elements in it so that the result is in fact a table. This + * assumes that in each of the iterations (time steps, nonlinear iterations, etc) + * you fill every single column. On the other hand, this may not always be what + * you want to do. For example, it could be that the function that computes the + * nonlinear residual is only called every few time steps; or, a function computing + * statistics of the mesh is only called whenever the mesh is in fact refined. + * + * In these cases, the add_value() function will be called less often for some + * columns and the column would therefore have fewer elements; furthermore, these + * elements would not be aligned with the rows that contain the other data elements + * that were produced during this iteration. + * + * To avoid this problem, we use the following algorithm: + * - When calling add_value(key, value), we count the number of elements + * in the column corresponding to key. Let's call this number $m$. + * - We also determine the maximal number of elements in the other columns; call + * it $n$. + * - If $m < n-1$ then we add $n-m-1$ copies of the object T() + * to this column. Here, T is the data type of the given value. + * For example, if T is a numeric type, then T() is + * the number zero; if T is std::string, then + * T() is the empty string "". + * - Add the given value to this column. + * + * Padding the column with default elements makes sure that after the addition the + * column has as many entries as the longest other column. In other words, if + * we have skipped previous invokations of add_value() for a given key, then the + * padding will enter default values into this column. + * + * The algorithm as described will fail if you try to skip adding values for a key + * if adding an element for this key is the first thing you want to do for a given + * iteration or time step, since we would then pad to the length of the longest + * column of the previous iteration or time step. You may have to re-order + * adding to this column to a different spot in your program, after adding to + * a column that will always be added to; or, you may want to start every iteration + * by adding the number of the iteration to the table, for example in column 1. + * + * * @ingroup textoutput * @author Ralf Hartmann, 1999; Wolfgang Bangerth, 2011 */ @@ -562,6 +615,12 @@ class TableHandler // inline and template functions namespace internal { + template + TableEntry::TableEntry (const T &t) + : + value (t) + {} + template inline T TableEntry::get () const @@ -714,6 +773,7 @@ template void TableHandler::add_value (const std::string &key, const T value) { + // see if the column already exists if (columns.find(key) == columns.end()) { std::pair new_column(key, Column(key)); @@ -721,8 +781,17 @@ void TableHandler::add_value (const std::string &key, column_order.push_back(key); } - const internal::TableEntry entry = { value }; - columns[key].entries.push_back (entry); + // follow the algorithm given in the introduction to this class + // of padding columns as necessary + unsigned int n = 0; + for (std::map< std::string, Column >::iterator p = columns.begin(); p != columns.end(); ++p) + n = (n >= p->second.entries.size() ? n : p->second.entries.size()); + + while (columns[key].entries.size()+1 < n) + columns[key].entries.push_back (internal::TableEntry(T())); + + // now push the value given to this function + columns[key].entries.push_back (internal::TableEntry(value)); } diff --git a/tests/base/table_handler_04.cc b/tests/base/table_handler_04.cc new file mode 100644 index 0000000000..a49a5b40b2 --- /dev/null +++ b/tests/base/table_handler_04.cc @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2010, 2011 by the deal.II authors +// +// This file is subject to QPL and may not be distributed +// without copyright and license information. Please refer +// to the file deal.II/doc/license.html for the text and +// further information on this license. +// +//----------------------------------------------------------------------------- + +// pad columns if we don't fill them in each iteration + + +#include "../tests.h" +#include +#include +#include + +#include +#include +#include +#include + + +int main () +{ + std::ofstream logfile("table_handler_04/output"); + deallog.attach(logfile); + deallog.depth_console(0); + deallog.threshold_double(1.e-10); + + TableHandler table; + + std::string keys[3] = { "key1", "key2", "key3" }; + + table.add_value(keys[0], 0); + table.add_value(keys[0], 1); + table.add_value(keys[0], 2); + table.add_value(keys[1], 13); + table.add_value(keys[2], std::string("a")); + + table.write_text(deallog.get_file_stream()); +} diff --git a/tests/base/table_handler_04/cmp/generic b/tests/base/table_handler_04/cmp/generic new file mode 100644 index 0000000000..66ba87d1d3 --- /dev/null +++ b/tests/base/table_handler_04/cmp/generic @@ -0,0 +1,5 @@ + +key1 key2 key3 +0 0 "" +1 0 "" +2 13 a