From 515db02cbab1426d2f8b2b3ca83ff2637db8d904 Mon Sep 17 00:00:00 2001 From: Timo Koch <timo.koch@iws.uni-stuttgart.de> Date: Fri, 29 Sep 2017 14:25:43 +0200 Subject: [PATCH] Implement logging parameter tree as alternative to paramproperties --- dumux/common/basicproperties.hh | 2 + dumux/common/loggingparametertree.hh | 229 ++++++++++++++++++ dumux/io/gridcreator.hh | 98 ++++---- test/common/CMakeLists.txt | 9 +- test/common/parameters/CMakeLists.txt | 2 + test/common/parameters/params.input | 6 + .../parameters/test_loggingparametertree.cc | 59 +++++ .../2p/implicit/incompressible/test_2p.cc | 11 +- 8 files changed, 353 insertions(+), 63 deletions(-) create mode 100644 dumux/common/loggingparametertree.hh create mode 100644 test/common/parameters/CMakeLists.txt create mode 100644 test/common/parameters/params.input create mode 100644 test/common/parameters/test_loggingparametertree.cc diff --git a/dumux/common/basicproperties.hh b/dumux/common/basicproperties.hh index 4c99830742..ff95c3827e 100644 --- a/dumux/common/basicproperties.hh +++ b/dumux/common/basicproperties.hh @@ -105,6 +105,7 @@ NEW_PROP_TAG(GridVariables); * The default is to not limit the step size. */ NEW_PROP_TAG(TimeLoopMaxTimeStepSize); +NEW_PROP_TAG(TimeManagerMaxTimeStepSize); //! the maximum allowed number of timestep divisions for the //! Newton solver @@ -135,6 +136,7 @@ SET_TYPE_PROP(NumericModel, PrimaryVariables, typename GET_PROP_TYPE(TypeTag, Nu //! use an unlimited time step size by default SET_SCALAR_PROP(NumericModel, TimeLoopMaxTimeStepSize, std::numeric_limits<typename GET_PROP_TYPE(TypeTag,Scalar)>::max()); +SET_SCALAR_PROP(NumericModel, TimeManagerMaxTimeStepSize, std::numeric_limits<typename GET_PROP_TYPE(TypeTag,Scalar)>::max()); //! set number of maximum timestep divisions to 10 SET_INT_PROP(NumericModel, TimeLoopMaxTimeStepDivisions, 10); diff --git a/dumux/common/loggingparametertree.hh b/dumux/common/loggingparametertree.hh new file mode 100644 index 0000000000..436e1f6b15 --- /dev/null +++ b/dumux/common/loggingparametertree.hh @@ -0,0 +1,229 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \brief A parameter tree that logs which parameters have been used + */ +#ifndef DUMUX_LOGGING_PARAMETER_TREE_HH +#define DUMUX_LOGGING_PARAMETER_TREE_HH + +#include <iomanip> +#include <iostream> +#include <string> + +#include <dune/common/parametertree.hh> + +namespace Dumux +{ + +/*! + * \ingroup Common + * \brief A parameter tree that logs which parameters have been used + */ +class LoggingParameterTree +{ + +public: + /* + * \brief A logging parameter tree is always attached to an existingparameter tree + */ + LoggingParameterTree() = delete; + + /* + * \brief Create LoggingParameterTree from ParameterTree + */ + LoggingParameterTree(const Dune::ParameterTree& params) + : params_(params) {} + + /** \brief test for key + * + * Tests whether given key exists. + * + * \param key key name + * \return true if key exists in structure, otherwise false + */ + bool hasKey(const std::string& key) const + { return params_.hasKey(key); } + + + /** \brief print the hierarchical parameter tree to stream + * + * \param stream the output stream to print to + */ + void report(std::ostream& stream = std::cout) const + { params_.report(stream); } + + /** \brief print distinct substructure to stream + * + * Prints all entries with given prefix. + * + * \param stream Stream to print to + * \param prefix for key and substructure names + */ + void reportAll(std::ostream& stream = std::cout) const + { + stream << "\n# Runtime-specified parameters used:" << std::endl; + usedRuntimeParams_.report(stream); + + const auto unusedParams = getUnusedKeys(); + if (!unusedParams.empty()) + { + stream << "\n# Unused parameters:" << std::endl; + for (const auto& key : unusedParams) + stream << key << " = \"" << params_[key] << "\"" << std::endl; + } + } + + + /** \brief get value as string + * + * Returns pure string value for given key. + * + * \param key key name + * \param defaultValue default if key does not exist + * \return value as string + * \note This might be quite slow so call it only once, + * e.g. on initialization of the class configured by runtime parameters + */ + std::string get(const std::string& key, const std::string& defaultValue) const + { + if (params_.hasKey(key)) + { + // log that we used this parameter + usedRuntimeParams_[key] = params_[key]; + return params_[key]; + } + + return defaultValue; + } + + /** \brief get value as string + * + * Returns pure string value for given key. + * + * \todo This is a hack so get("my_key", "xyz") compiles + * (without this method "xyz" resolves to bool instead of std::string) + * \param key key name + * \param defaultValue default if key does not exist + * \return value as string + * \note This might be quite slow so call it only once, + * e.g. on initialization of the class configured by runtime parameters + */ + std::string get(const std::string& key, const char* defaultValue) const + { + if (params_.hasKey(key)) + { + // log that we used this parameter + usedRuntimeParams_[key] = params_[key]; + return params_[key]; + } + + return defaultValue; + } + + + /** \brief get value converted to a certain type + * + * Returns value as type T for given key. + * + * \tparam T type of returned value. + * \param key key name + * \param defaultValue default if key does not exist + * \return value converted to T + * \note This might be quite slow so call it only once, + * e.g. on initialization of the class configured by runtime parameters + */ + template<typename T> + T get(const std::string& key, const T& defaultValue) const + { + if (params_.hasKey(key)) + { + // log that we used this parameter + usedRuntimeParams_[key] = params_[key]; + return params_.template get<T>(key); + } + + return defaultValue; + } + + /** \brief Get value + * + * \tparam T Type of the value + * \param key Key name + * \throws RangeError if key does not exist + * \throws NotImplemented Type is not supported + * \return value as T + * \note This might be quite slow so call it only once, + * e.g. on initialization of the class configured by runtime parameters + */ + template <class T> + T get(const std::string& key) const + { + if (params_.hasKey(key)) + { + // log that we used this parameter + usedRuntimeParams_[key] = params_[key]; + } + + return params_.template get<T>(key); + } + + /** \brief Find the keys that haven't been used yet + * + * \retuns unusedParams Container storing unused keys + * \note Useful for debugging purposes + */ + std::vector<std::string> getUnusedKeys() const + { + std::vector<std::string> unusedParams; + findUnusedKeys(params_, unusedParams); + return unusedParams; + } + +private: + /** \brief Find the keys that haven't been used yet recursively + * + * \param tree The tree to look in for unused keys + * \param unusedParams Container to store unused keys + * \param prefix the prefix attached to the key + */ + void findUnusedKeys(const Dune::ParameterTree& tree, + std::vector<std::string>& unusedParams, + const std::string& prefix = "") const + { + // loop over all keys of the current tree + // store keys which were not accessed + const auto& keys = tree.getValueKeys(); + for (const auto& key : keys) + if (!usedRuntimeParams_.hasKey(prefix + key)) + unusedParams.push_back(prefix + key); + + // recursively loop over all subtrees + const auto& subTreeKeys = tree.getSubKeys(); + for (const auto& key : subTreeKeys) + findUnusedKeys(tree.sub(key), unusedParams, prefix + key + "."); + } + + const Dune::ParameterTree& params_; + mutable Dune::ParameterTree usedRuntimeParams_; +}; + +} // end namespace Dumux + +#endif diff --git a/dumux/io/gridcreator.hh b/dumux/io/gridcreator.hh index e12a9cce72..d0f61feaca 100644 --- a/dumux/io/gridcreator.hh +++ b/dumux/io/gridcreator.hh @@ -338,14 +338,11 @@ protected: /*! * \brief Refines a grid after construction if GridParameterGroup.Refinement is set in the input file */ - static void maybeRefineGrid() + template<class Parameters> + static void maybeRefineGrid(const Parameters& params, const std::string& gridKey) { - try { - const int level = GET_RUNTIME_PARAM_FROM_GROUP_CSTRING(TypeTag, int, GET_PROP_VALUE(TypeTag, GridParameterGroup).c_str(), Refinement); - grid().globalRefine(level); - } - catch (ParameterException &e) {} - catch (...) { throw; } + if (params.hasKey(gridKey + ".Refinement")) + grid().globalRefine(params.template get<int>(gridKey + ".Refinement")); } /*! @@ -391,7 +388,8 @@ public: /*! * \brief Make the grid. This is implemented by specializations of this class. */ - static void makeGrid() + template<class Parameters> + static void makeGrid(const Parameters& params) { DUNE_THROW(Dune::NotImplemented, "The GridCreator for grid type " << Dune::className<Grid>() << " is not implemented! Consider providing your own GridCreator."); @@ -480,74 +478,66 @@ public: /*! * \brief Make the grid. This is implemented by specializations of this method. */ - static void makeGrid() + template<class Parameters> + static void makeGrid(const Parameters& params) { // First try to create it from a DGF file in GridParameterGroup.File - try { - const std::string fileName = GET_RUNTIME_PARAM_FROM_GROUP_CSTRING(TypeTag, std::string, GET_PROP_VALUE(TypeTag, GridParameterGroup).c_str(), File); - ParentType::makeGridFromDgfFile(fileName); - postProcessing_(); + const std::string gridKey = params.get("GridParameterGroup", "Grid"); + if (params.hasKey(gridKey + ".File")) + { + ParentType::makeGridFromDgfFile(params.template get<std::string>(gridKey + ".File")); + postProcessing_(params, gridKey); return; } - catch (ParameterException &e) {} - catch (...) { throw; } // Then look for the necessary keys to construct from the input file - try { - // The required parameters - typedef Dune::FieldVector<ct, dim> GlobalPosition; - const GlobalPosition upperRight = GET_RUNTIME_PARAM_FROM_GROUP_CSTRING(TypeTag, GlobalPosition, GET_PROP_VALUE(TypeTag, GridParameterGroup).c_str(), UpperRight); + if (!params.hasKey(gridKey + ".UpperRight")) + DUNE_THROW(ParameterException, "Please supply the mandatory parameter " + << gridKey << ".UpperRight or a grid file in " + << gridKey << ".File."); - // The optional parameters (they have a default) - typedef std::array<int, dim> CellArray; - CellArray cells; - std::fill(cells.begin(), cells.end(), 1); - try { cells = GET_RUNTIME_PARAM_FROM_GROUP_CSTRING(TypeTag, CellArray, GET_PROP_VALUE(TypeTag, GridParameterGroup).c_str(), Cells); } - catch (ParameterException &e) { } + // get the upper right corner coordinates + const auto upperRight = params.template get<Dune::FieldVector<ct, dim>>(gridKey + ".UpperRight"); - typedef std::bitset<dim> BitSet; - BitSet periodic; - try { periodic = GET_RUNTIME_PARAM_FROM_GROUP_CSTRING(TypeTag, BitSet, GET_PROP_VALUE(TypeTag, GridParameterGroup).c_str(), Periodic);} - catch (ParameterException &e) { } + // number of cells in each direction + std::array<int, dim> cells; cells.fill(1); + cells = params.template get<std::array<int, dim>>(gridKey + ".Cells", cells); - // get the overlap dependent on some template parameters - int overlap = YaspOverlapHelper<TypeTag>::getOverlap(); + // periodic boundaries + const auto periodic = params.template get<std::bitset<dim>>(gridKey + ".Periodic", std::bitset<dim>()); - bool default_lb = false; - CellArray partitioning; - try { partitioning = GET_RUNTIME_PARAM_FROM_GROUP_CSTRING(TypeTag, CellArray, GET_PROP_VALUE(TypeTag, GridParameterGroup).c_str(), Partitioning);} - catch (ParameterException &e) { default_lb = true; } + // get the overlap dependent on some template parameters + const int overlap = YaspOverlapHelper<TypeTag>::getOverlap(); - //make the grid - if (default_lb) - ParentType::gridPtr() = std::make_shared<Grid>(upperRight, cells, periodic, overlap); - else - { - typename Dune::YaspFixedSizePartitioner<dim> lb(partitioning); - ParentType::gridPtr() = std::make_shared<Grid>(upperRight, cells, periodic, overlap, typename Grid::CollectiveCommunicationType(), &lb); - } - postProcessing_(); + bool default_lb = !params.hasKey(gridKey + ".Partitioning"); + + // make the grid + if (default_lb) + { + // construct using default load balancing + ParentType::gridPtr() = std::make_shared<Grid>(upperRight, cells, periodic, overlap); } - catch (ParameterException &e) { - DUNE_THROW(ParameterException, "Please supply the mandatory parameter " - << GET_PROP_VALUE(TypeTag, GridParameterGroup) << ".UpperRight or a grid file in " - << GET_PROP_VALUE(TypeTag, GridParameterGroup) << ".File."); + else + { + // construct using user defined partitioning + const auto partitioning = params.template get<std::array<int, dim>>(gridKey + ".Partitioning"); + Dune::YaspFixedSizePartitioner<dim> lb(partitioning); + ParentType::gridPtr() = std::make_shared<Grid>(upperRight, cells, periodic, overlap, typename Grid::CollectiveCommunicationType(), &lb); } - catch (...) { throw; } + postProcessing_(params, gridKey); } private: /*! * \brief Postprocessing for YaspGrid */ - static void postProcessing_() + template<class Parameters> + static void postProcessing_(const Parameters& params, const std::string& gridKey) { // Check if should refine the grid - bool keepPhysicalOverlap = true; - try { keepPhysicalOverlap = GET_RUNTIME_PARAM_FROM_GROUP_CSTRING(TypeTag, bool, GET_PROP_VALUE(TypeTag, GridParameterGroup).c_str(), KeepPhysicalOverlap);} - catch (ParameterException &e) { } + bool keepPhysicalOverlap = params.template get<bool>(gridKey + ".KeepPhysicalOverlap", true); ParentType::grid().refineOptions(keepPhysicalOverlap); - ParentType::maybeRefineGrid(); + ParentType::maybeRefineGrid(params, gridKey); } }; diff --git a/test/common/CMakeLists.txt b/test/common/CMakeLists.txt index 1fed13a61f..c1ef7651ad 100644 --- a/test/common/CMakeLists.txt +++ b/test/common/CMakeLists.txt @@ -1,4 +1,5 @@ -add_subdirectory("generalproblem") -add_subdirectory("propertysystem") -add_subdirectory("spline") -add_subdirectory("boundingboxtree") +add_subdirectory(generalproblem) +add_subdirectory(propertysystem) +add_subdirectory(spline) +add_subdirectory(boundingboxtree) +add_subdirectory(parameters) diff --git a/test/common/parameters/CMakeLists.txt b/test/common/parameters/CMakeLists.txt new file mode 100644 index 0000000000..45293e09ce --- /dev/null +++ b/test/common/parameters/CMakeLists.txt @@ -0,0 +1,2 @@ +dune_add_test(SOURCES test_loggingparametertree.cc) +dune_symlink_to_source_files(FILES "params.input") diff --git a/test/common/parameters/params.input b/test/common/parameters/params.input new file mode 100644 index 0000000000..c86b64973b --- /dev/null +++ b/test/common/parameters/params.input @@ -0,0 +1,6 @@ +[TimeLoop] +TEnd = 1e6 + +[Grid] +Cells = 100 100 +Bells = 10 10 diff --git a/test/common/parameters/test_loggingparametertree.cc b/test/common/parameters/test_loggingparametertree.cc new file mode 100644 index 0000000000..6af28f6398 --- /dev/null +++ b/test/common/parameters/test_loggingparametertree.cc @@ -0,0 +1,59 @@ +#include <config.h> +#include <iostream> + +#include <dune/common/parametertreeparser.hh> +#include <dune/common/parallel/mpihelper.hh> +#include <dune/common/exceptions.hh> + +#include <dumux/common/parameterparser.hh> +#include <dumux/common/loggingparametertree.hh> + +int main (int argc, char *argv[]) try +{ + // maybe initialize mpi + Dune::MPIHelper::instance(argc, argv); + + // parse the input file into the parameter tree + Dune::ParameterTree tree; + Dumux::ParameterParser::parseInputFile(argc, argv, tree, "params.input"); + + // attach the tree to the logging tree + Dumux::LoggingParameterTree params(tree); + + // use some default parameters + bool DUNE_UNUSED(enableGravity) = params.get<bool>("Problem.EnableGravity", true); + + // use some given parameters + const auto DUNE_UNUSED(cells) = params.get<std::array<int, 2>>("Grid.Cells", {1, 1}); + const auto DUNE_UNUSED(tEnd) = params.get<double>("TimeLoop.TEnd"); + + // check the unused keys + const auto unused = params.getUnusedKeys(); + if (unused.size() != 1) + DUNE_THROW(Dune::InvalidStateException, "There should be exactly one unused key!"); + else if (unused[0] != "Grid.Bells") + DUNE_THROW(Dune::InvalidStateException, "Unused key \"Grid.Bells\" not found!"); + + params.reportAll(); + + return 0; +} +// ////////////////////////////////// +// Error handler +// ///////////////////////////////// +catch (const Dune::RangeError &e) { + std::cout << e << std::endl; + return 1; +} +catch (const Dune::Exception& e) { + std::cout << e << std::endl; + return 1; +} +catch (const std::exception& e) { + std::cout << e.what() << std::endl; + return 1; +} +catch (...) { + std::cout << "Unknown exception!" << std::endl; + return 1; +} diff --git a/test/porousmediumflow/2p/implicit/incompressible/test_2p.cc b/test/porousmediumflow/2p/implicit/incompressible/test_2p.cc index 5063cdb9d2..fe62b55b56 100644 --- a/test/porousmediumflow/2p/implicit/incompressible/test_2p.cc +++ b/test/porousmediumflow/2p/implicit/incompressible/test_2p.cc @@ -40,6 +40,7 @@ #include <dumux/common/dumuxmessage.hh> #include <dumux/common/defaultusagemessage.hh> #include <dumux/common/parameterparser.hh> +#include <dumux/common/loggingparametertree.hh> #include <dumux/linear/seqsolverbackend.hh> #include <dumux/nonlinear/newtonmethod.hh> @@ -89,16 +90,13 @@ int main(int argc, char** argv) try // check first if the user provided an input file through the command line, if not use the default const auto parameterFileName = ParameterTree::tree().hasKey("ParameterFile") ? GET_RUNTIME_PARAM(TypeTag, std::string, ParameterFile) : ""; ParameterParser::parseInputFile(argc, argv, ParameterTree::tree(), parameterFileName); + LoggingParameterTree params(ParameterTree::tree()); ////////////////////////////////////////////////////////////////////// // try to create a grid (from the given grid file or the input file) ///////////////////////////////////////////////////////////////////// - try { GridCreator::makeGrid(); } - catch (...) { - std::cout << "\n\t -> Creation of the grid failed! <- \n\n"; - throw; - } + GridCreator::makeGrid(params); GridCreator::loadBalance(); //////////////////////////////////////////////////////////// @@ -211,7 +209,10 @@ int main(int argc, char** argv) try // print dumux end message if (mpiHelper.rank() == 0) + { + params.reportAll(); DumuxMessage::print(/*firstCall=*/false); + } return 0; -- GitLab