diff --git a/.gitmodules b/.gitmodules index 112a0edd3e7..9828eb980ba 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "libs/h5xx"] path = libs/h5xx url = https://github.com/h5md/h5xx.git +[submodule "libs/boost_matheval"] + path = libs/boost_matheval + url = https://github.com/hmenke/boost_matheval.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 3fe4873011b..e887229c0bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -248,6 +248,19 @@ if(WITH_VALGRIND_INSTRUMENTATION) endif(VALGRIND_FOUND) endif(WITH_VALGRIND_INSTRUMENTATION) +# Check out the Boost Matheval submodule +if(NOT EXISTS "${CMAKE_SOURCE_DIR}/libs/boost_matheval/.git") + # Try to find git + find_package(Git) + if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git") + message(STATUS "Initializing/updating Boost Matheval") + execute_process( + COMMAND ${GIT_EXECUTABLE} submodule update --init -- libs/boost_matheval + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + endif() +endif() +include_directories(${CMAKE_SOURCE_DIR}/libs/boost_matheval/include) + include(RequireCXX11) ####################################################################### diff --git a/doc/doxygen/main.dox b/doc/doxygen/main.dox index 849aff5aba4..66b6b312494 100755 --- a/doc/doxygen/main.dox +++ b/doc/doxygen/main.dox @@ -50,7 +50,7 @@ i. e. a client-server model is utilized. More details can be found in however, the communication is done synchronously. For more details see \ref integrate.cpp "integrate.cpp". -\section Copyright and License of the Code Documentation +\section copyright Copyright and License of the Code Documentation Copyright (C) 2010,2011,2012 The ESPResSo project Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010 Max-Planck-Institute for Polymer Research, Theory Group diff --git a/libs/boost_matheval b/libs/boost_matheval new file mode 160000 index 00000000000..72edf4cf45a --- /dev/null +++ b/libs/boost_matheval @@ -0,0 +1 @@ +Subproject commit 72edf4cf45ac8c71511c0fea97878906f473b7aa diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b3ae97452f9..afe9a80ca29 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,6 +1,4 @@ -file(GLOB EspressoCore_SRC - "*.cpp" - ) +file(GLOB EspressoCore_SRC "*.cpp" "utils/*.cpp") if( WITH_COVERAGE ) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Og --coverage -fprofile-arcs -ftest-coverage") diff --git a/src/core/unit_tests/CMakeLists.txt b/src/core/unit_tests/CMakeLists.txt index 65c5bf6b877..bc8951e0f54 100644 --- a/src/core/unit_tests/CMakeLists.txt +++ b/src/core/unit_tests/CMakeLists.txt @@ -85,3 +85,4 @@ unit_test(NAME for_each_pair_test SRC for_each_pair_test.cpp) unit_test(NAME all_compare_test SRC all_compare_test.cpp NUM_PROC 3) unit_test(NAME None_test SRC None_test.cpp) unit_test(NAME keys_test SRC keys_test.cpp) +unit_test(NAME ExpressionParser_test SRC ExpressionParser_test.cpp ../utils/ExpressionParser.cpp) diff --git a/src/core/unit_tests/ExpressionParser_test.cpp b/src/core/unit_tests/ExpressionParser_test.cpp new file mode 100644 index 00000000000..183449ef43a --- /dev/null +++ b/src/core/unit_tests/ExpressionParser_test.cpp @@ -0,0 +1,72 @@ +/* + Copyright (C) 2017 The ESPResSo project + + This file is part of ESPResSo. + + ESPResSo 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 3 of the License, or + (at your option) any later version. + + ESPResSo 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 . +*/ + +#define BOOST_TEST_MODULE Expression parser test +#define BOOST_TEST_DYN_LINK +#include + +#include +#include +#include +#include +#include + +#include "utils/ExpressionParser.hpp" + +// We only test the integration into ESPResSo as Boost Matheval has +// plenty of tests itself. + +BOOST_AUTO_TEST_CASE(integration1) { + std::string expr = "cbrt(x/2 + sqrt(x**2/4 + y**3/24))"; + double x = 2.0, y = -1.0; + + Utils::ExpressionParser parser; + BOOST_CHECK_NO_THROW(parser.parse(expr)); + + std::map symbol_table = { + std::make_pair("x",x), + std::make_pair("y",y), + }; + + double result = 0; + BOOST_CHECK_NO_THROW(result = parser.evaluate(symbol_table)); + + double expected = std::cbrt(x/2. + std::sqrt(std::pow(x,2.)/4. + std::pow(y,3.)/24.)); + BOOST_CHECK_CLOSE_FRACTION(result, expected, std::numeric_limits::epsilon()); +} + +BOOST_AUTO_TEST_CASE(integration2) { + std::string expr = "("; + + Utils::ExpressionParser parser; + + // Parsing should fail + BOOST_CHECK_THROW(parser.parse(expr), std::runtime_error); +} + +BOOST_AUTO_TEST_CASE(integration3) { + std::string expr = "x"; + + Utils::ExpressionParser parser; + + BOOST_CHECK_NO_THROW(parser.parse(expr)); + + // Evaluating should fail + BOOST_CHECK_THROW(parser.evaluate({}), std::invalid_argument); +} diff --git a/src/core/utils/ExpressionParser.cpp b/src/core/utils/ExpressionParser.cpp new file mode 100644 index 00000000000..83e4b0f0ed6 --- /dev/null +++ b/src/core/utils/ExpressionParser.cpp @@ -0,0 +1,61 @@ +/* + Copyright (C) 2017 The ESPResSo project + + This file is part of ESPResSo. + + ESPResSo 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 3 of the License, or + (at your option) any later version. + + ESPResSo 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 . +*/ +#include +#include + +#include "ExpressionParser.hpp" +#include "utils/make_unique.hpp" + +#include "matheval.hpp" + +namespace Utils +{ + +class ExpressionParser::impl +{ + matheval::Parser parser; +public: + void parse(std::string const &expr) + { + parser.parse(expr); + } + + double evaluate(std::map const &st) + { + return parser.evaluate(st); + } +}; + +ExpressionParser::ExpressionParser() + : pimpl{Utils::make_unique()} +{} + +ExpressionParser::~ExpressionParser() {} + +void ExpressionParser::parse(std::string const &expr) +{ + pimpl->parse(expr); +} + +double ExpressionParser::evaluate(std::map const &st) +{ + return pimpl->evaluate(st); +} + +} // namespace Utils diff --git a/src/core/utils/ExpressionParser.hpp b/src/core/utils/ExpressionParser.hpp new file mode 100644 index 00000000000..607dffbc437 --- /dev/null +++ b/src/core/utils/ExpressionParser.hpp @@ -0,0 +1,72 @@ +/* + Copyright (C) 2017 The ESPResSo project + + This file is part of ESPResSo. + + ESPResSo 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 3 of the License, or + (at your option) any later version. + + ESPResSo 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 . +*/ +#ifndef CORE_UTILS_EXPRESSION_PARSER_HPP +#define CORE_UTILS_EXPRESSION_PARSER_HPP + +#include +#include +#include + +namespace Utils +{ + +/** @brief Parse a mathematical expression + * + * This can parse and evaluate a mathematical expression for a given + * symbol table using Boost Matheval. The templates of Boost.Spirit + * (which is the basis of Boost Matheval) are very expensive to parse + * and instantiate, which is why we hide it behind an opaque pointer. + * + * The drawback of this approach is that calls can no longer be + * inlined and because the pointer crosses translation unit + * boundaries, dereferencing it can also not be optimized out at + * compile time. We have to rely entirely on link-time optimization + * which might be not as good. + * + * The pointer to the implementation is a std::unique_ptr which makes + * the class not copyable but only moveable. Copying shouldn't be + * required but is easy to implement. + */ +class ExpressionParser +{ + class impl; + std::unique_ptr pimpl; +public: + /** @brief Constructor */ + ExpressionParser(); + + /** @brief Destructor */ + ~ExpressionParser(); + + /** @brief Parse the mathematical expression into an abstract syntax tree + * + * @param[in] expr The expression given as a std::string + */ + void parse(std::string const &expr); + + /** @brief Evaluate the abstract syntax tree for a given symbol table + * + * @param[in] st The symbol table + */ + double evaluate(std::map const &st); +}; + +} // namespace Utils + +#endif // CORE_UTILS_EXPRESSION_PARSER_HPP diff --git a/src/core/utils/Timer.cpp b/src/core/utils/Timer.cpp index 384da5236ec..fa36a156a82 100644 --- a/src/core/utils/Timer.cpp +++ b/src/core/utils/Timer.cpp @@ -30,7 +30,7 @@ namespace Timing { std::vector> Timer::get_all_stats() { - boost::mpi::communicator const &comm = Communication::mpiCallbacks().comm(); + boost::mpi::communicator const &comm = Communication::MpiCallbacks(comm_cart).comm(); assert(comm.rank() == 0); std::vector> ret( @@ -38,7 +38,7 @@ Timer::get_all_stats() { ret[0] = get_stats(); - Communication::mpiCallbacks().call(Timer::mpi_callback); + Communication::MpiCallbacks(comm_cart).call(Timer::mpi_callback); for (unsigned i = 1; i < ret.size(); ++i) { comm.recv(i, 42, ret[i]); @@ -48,11 +48,9 @@ Timer::get_all_stats() { } void Timer::mpi_callback(int, int) { - Communication::mpiCallbacks().comm().send(0, 42, get_stats()); + Communication::MpiCallbacks(comm_cart).comm().send(0, 42, get_stats()); } std::unordered_map Timer::m_timers; - -const Communication::CallbackAdder Timer::cb_adder{Timer::mpi_callback}; } } diff --git a/src/core/utils/Timer.hpp b/src/core/utils/Timer.hpp index 31eecc166a6..1c8e465f6dc 100644 --- a/src/core/utils/Timer.hpp +++ b/src/core/utils/Timer.hpp @@ -9,6 +9,7 @@ #include +#include "communication.hpp" #include "MpiCallbacks.hpp" #include "utils/statistics/RunningAverage.hpp" @@ -95,8 +96,6 @@ class Timer { friend Communication::MpiCallbacks; static void mpi_callback(int, int); - static const Communication::CallbackAdder cb_adder; - static std::unordered_map m_timers; Statistics::RunningAverage m_running_average; double m_mark;