From 16bae31827f2075f70261e8ce4b57079a475649e Mon Sep 17 00:00:00 2001
From: acrovato <a.crovato@uliege.be>
Date: Fri, 3 Jul 2020 12:26:12 +0200
Subject: [PATCH] Restart clean. fpm as submodule of waves (depends on fwk and
 tbox). Add timers.

---
 .clang-format                        |  8 +++
 .gitlab-ci.yml                       | 23 +++++--
 fpm/_src/CMakeLists.txt              | 11 +++-
 fpm/_src/fpmw.h                      |  2 +-
 fpm/_src/fpmw.i                      | 31 +++++----
 fpm/_src/fpmw.swg                    | 80 -----------------------
 fpm/src/CMakeLists.txt               |  6 +-
 fpm/src/fObject.h                    | 75 ---------------------
 fpm/src/fTimers.cpp                  | 97 ++++++++++++++++++++++++++++
 fpm/src/fTimers.h                    | 76 ++++++++++++++++++++++
 fpm/src/{fObject.cpp => fTimers.inl} | 36 ++++-------
 fpm/src/fpm.h                        | 19 ++----
 fpm/tests/{basic.py => timers.py}    | 29 +++++++--
 13 files changed, 269 insertions(+), 224 deletions(-)
 create mode 100644 .clang-format
 delete mode 100644 fpm/_src/fpmw.swg
 delete mode 100644 fpm/src/fObject.h
 create mode 100644 fpm/src/fTimers.cpp
 create mode 100644 fpm/src/fTimers.h
 rename fpm/src/{fObject.cpp => fTimers.inl} (60%)
 rename fpm/tests/{basic.py => timers.py} (55%)

diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..e5fd772
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,8 @@
+# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
+BasedOnStyle: LLVM
+UseTab: Never
+IndentWidth: 4
+BreakBeforeBraces: Allman
+SortIncludes: false
+ColumnLimit: 0
+AccessModifierOffset: -4
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 6e1c814..90501d3 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,7 +1,7 @@
 # gitlab-ci file for fpm
 
 default:
-    image: rboman/waves-py3:2020.1
+    image: rboman/waves-py3:2020.3
     before_script:
         - source /opt/intel/mkl/bin/mklvars.sh intel64
         - source /opt/intel/tbb/bin/tbbvars.sh intel64
@@ -11,14 +11,26 @@ stages:
     - test
 
 build:
-    image: rboman/waves-py3:2020.1
+    image: rboman/waves-py3:2020.3
     stage: build
     script:
         - printenv | sort
+        - cd ..
+        - rm -rf waves
+        - wget -q https://gitlab.uliege.be/am-dept/waves/-/archive/feature_fpm/waves-feature_fpm.tar.bz2
+        - tar xf waves-feature_fpm.tar.bz2
+        - rm waves-feature_fpm.tar.bz2
+        - mv waves-feature_fpm waves
+        - cd waves
+        - mkdir build
+        - cd build
+        - cmake -Wno-dev -C ../CMake/disable-trilinos.cmake ..
+        - make -j $(nproc)
+        - cd ../../fpm
         - rm -rf build workspace
         - mkdir build
         - cd build
-        - cmake -Wno-dev ..
+        - cmake -DCMAKE_PREFIX_PATH=../../waves -Wno-dev ..
         - make -j $(nproc)
     artifacts:
         paths:
@@ -37,8 +49,8 @@ doxygen:
     dependencies:
         - build
 
-ctest-py3:
-    image: rboman/waves-py3:2020.1
+ctest:
+    image: rboman/waves-py3:2020.3
     stage: test
     script:
         - cd build
@@ -46,4 +58,3 @@ ctest-py3:
     #timeout: 10 hours  # will be available in 12.3
     dependencies:
         - build
-
diff --git a/fpm/_src/CMakeLists.txt b/fpm/_src/CMakeLists.txt
index 059425a..a137601 100644
--- a/fpm/_src/CMakeLists.txt
+++ b/fpm/_src/CMakeLists.txt
@@ -22,8 +22,16 @@ FILE(GLOB ISRCS *.i)
 SET(CMAKE_SWIG_FLAGS "")
 SET_SOURCE_FILES_PROPERTIES(${ISRCS} PROPERTIES CPLUSPLUS ON)
 
+FOREACH(dir ${WAVES_INCLUDE_DIRS})
+    LIST(APPEND WAVES_SINCLUDE_DIRS "-I${dir}")
+ENDFOREACH()
+FOREACH(dir ${WAVES_SWIG_DIRS})
+    LIST(APPEND WAVES_SINCLUDE_DIRS "-I${dir}")
+ENDFOREACH()
+
 SET(SWINCFLAGS
 -I${PROJECT_SOURCE_DIR}/fpm/src
+${WAVES_SINCLUDE_DIRS}
 )
 SET_SOURCE_FILES_PROPERTIES(${ISRCS} PROPERTIES SWIG_FLAGS "${SWINCFLAGS}")
 
@@ -35,10 +43,11 @@ endif()
 MACRO_DebugPostfix(_fpmw)
 
 TARGET_INCLUDE_DIRECTORIES(_fpmw PRIVATE ${PROJECT_SOURCE_DIR}/fpm/_src
+                                         ${WAVES_SWIG_DIRS}
                                          ${PYTHON_INCLUDE_PATH}
 )
 
 SWIG_LINK_LIBRARIES(fpmw
-                    fpm ${PYTHON_LIBRARIES}
+                    fpm ${WAVES_LIBRARIES} ${PYTHON_LIBRARIES}
 )
 
diff --git a/fpm/_src/fpmw.h b/fpm/_src/fpmw.h
index 5967301..ce160ec 100644
--- a/fpm/_src/fpmw.h
+++ b/fpm/_src/fpmw.h
@@ -14,4 +14,4 @@
  * limitations under the License.
  */
 
-#include "fObject.h"
+#include "fTimers.h"
diff --git a/fpm/_src/fpmw.i b/fpm/_src/fpmw.i
index 2da4432..a9bb68b 100644
--- a/fpm/_src/fpmw.i
+++ b/fpm/_src/fpmw.i
@@ -36,34 +36,33 @@ threads="1"
 #include <sstream>
 #include <typeinfo>
 
-#include "fpm.h"
+#include "fwkw.h"
+#include "tboxw.h"
 #include "fpmw.h"
 
+#include "fpm.h"
+
 %}
 
-%include "fpmw.swg"
+%include "fwkw.swg"
+%import "tboxw.i"
 
 %include "fpm.h"
 
-%include <std_shared_ptr.i>
-
-%shared_ptr(fpm::fSharedObject);
-%include "fObject.h"
+%include "fTimers.h"
 
 namespace fpm {
-
-%extend fObject {
-    std::string __str__() {
-        std::stringstream str; str << *self;
-        return str.str();
+%extend Timers {
+    Timer &getitem(std::string const &name)
+    {
+        return (*self)[name];
     }
-}
 
-%extend fSharedObject {
-    std::string __str__() {
-        std::stringstream str; str << *self;
-        return str.str();
+    %pythoncode {
+def __getitem__(self, name):
+    return self.getitem(name)
     }
 }
 };
 
+//%include <std_shared_ptr.i>
diff --git a/fpm/_src/fpmw.swg b/fpm/_src/fpmw.swg
deleted file mode 100644
index 169ce32..0000000
--- a/fpm/_src/fpmw.swg
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2020 University of Liège
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- 
- // common includes
-
-// !! mingw + c++11 + python:
-// cmath doit être inclus AVANT Python.h (et en particulier pyconfig.h)
-// car il renomme hypot en _hypoth (bidouille liée au visual)
-%begin %{
-#if defined(_WIN32) && defined(__GNUC__)
-#include <cmath>
-#endif
-%}
-
-// petite bidouille pour pouvoir compiler avec "threads=1" et iterer sur des std_vector
-// (sinon ca explose � la destruction de l'iterateur)
-%nothread swig::SwigPyIterator::~SwigPyIterator();
-
-%include "std_string.i"
-%include "exception.i"
-%include "std_vector.i"
-%include "std_list.i"
-%include "std_map.i"
-
-// from: http://swig.10945.n7.nabble.com/Trapping-Swig-DirectorException-td6013.html
-// le code suivant permet de voir la call stack dans les appels C++ => python
-
-%{ 
-   static void handle_exception(void) { 
-     try { 
-       throw; 
-     } catch (std::exception &e) { 
-        std::stringstream txt; 
-        txt << e.what(); // << ' ' << typeid(e).name();
-        PyErr_SetString(PyExc_RuntimeError, e.what()); 
-     } 
-     catch(...) 
-     {
-        PyErr_SetString(PyExc_RuntimeError, "Unknown C++ Runtime Error");
-     } 
-   } 
-%} 
-
-%exception { 
-   try { 
-     $action 
-   } catch (...) { 
-     // Note that if a director method failed, the Python error indicator 
-     // already contains full details of the exception, and it will be 
-     // reraised when we go to SWIG_fail; so no need to convert the C++ 
-     // exception back to a Python one 
-     if (!PyErr_Occurred()) { 
-       handle_exception(); 
-     } 
-     SWIG_fail; 
-   } 
-} 
-
-%feature("director:except") { 
-   if ($error != NULL) { 
-     throw Swig::DirectorMethodException(); 
-   } 
-} 
-
-
-%ignore operator<<;
-
diff --git a/fpm/src/CMakeLists.txt b/fpm/src/CMakeLists.txt
index e19d83a..54ef33c 100644
--- a/fpm/src/CMakeLists.txt
+++ b/fpm/src/CMakeLists.txt
@@ -32,5 +32,9 @@ FIND_PACKAGE(EIGEN 3.3.4 REQUIRED)
 TARGET_INCLUDE_DIRECTORIES(fpm PUBLIC ${EIGEN_INCLUDE_DIRS})
 TARGET_COMPILE_DEFINITIONS(fpm PUBLIC EIGEN_DONT_PARALLELIZE)
 
-SOURCE_GROUP(base       REGULAR_EXPRESSION ".*\\.(cpp|inl|hpp|h)")
+# -- WAVES
+FIND_PACKAGE(WAVES REQUIRED)
+TARGET_INCLUDE_DIRECTORIES(fpm PUBLIC ${WAVES_INCLUDE_DIRS})
+TARGET_LINK_LIBRARIES(fpm ${WAVES_LIBRARIES})
 
+SOURCE_GROUP(base       REGULAR_EXPRESSION ".*\\.(cpp|inl|hpp|h)")
diff --git a/fpm/src/fObject.h b/fpm/src/fObject.h
deleted file mode 100644
index 27a6948..0000000
--- a/fpm/src/fObject.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2020 University of Liège
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef FOBJECT_H
-#define FOBJECT_H
-
-#include "fpm.h"
-#include <iostream>
-
-namespace fpm
-{
-
-/**
- * @brief Base class of all virtual objects
- *
- *        The only purpose of this class is that it avoids many copy/paste for
- *        the print function (__str__() fct) in SWIG/python
- */
-
-class FPM_API fObject
-{
-public:
-    fObject() = default;
-    virtual ~fObject() {}
-    fObject(const fObject &) = delete;
-    fObject &operator=(const fObject &) = delete;
-
-#ifndef SWIG
-    friend FPM_API std::ostream &operator<<(std::ostream &out, fObject const &obj);
-    virtual void write(std::ostream &out) const;
-#endif
-};
-
-/**
- * @brief Base class of smart-pointed objects (shared_ptr)
- *
- *        This base class is only required for "print" in SWIG/python.
- *        std::cout << *o works from C++ (it calls "write") even if the object inherits from wObject
- *        which is not defined in SWIG as shared_ptr-managed object
- *        However "print o" fails in python if the shared object inherits from wObject.
- *        This class also avoids a warning in SWIG telling that some derived or base classes are not
- *        managed by shared_ptr.
- */
-
-class FPM_API fSharedObject
-{
-public:
-    fSharedObject() = default;
-    virtual ~fSharedObject() {}
-    fSharedObject(const fSharedObject &) = delete;
-    fSharedObject &operator=(const fSharedObject &) = delete;
-
-#ifndef SWIG
-    friend FPM_API std::ostream &operator<<(std::ostream &out, fSharedObject const &obj);
-    virtual void write(std::ostream &out) const;
-#endif
-};
-
-} // namespace fpm
-
-#endif //FOBJECT_H
-
diff --git a/fpm/src/fTimers.cpp b/fpm/src/fTimers.cpp
new file mode 100644
index 0000000..f2d6838
--- /dev/null
+++ b/fpm/src/fTimers.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2020 University of Liège
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fTimers.h"
+#include <iomanip>
+
+using namespace fpm;
+
+Timer::Timer() : isOn(false)
+{
+    reset();
+}
+
+/**
+ * @brief Reset the timer
+ */
+void Timer::reset()
+{
+    if (!isOn)
+    {
+        start();
+        stop();
+    }
+    else
+        throw std::runtime_error("Cannot reset a running timer!\n");
+}
+
+/**
+ * @brief Start the timer
+ */
+void Timer::start()
+{
+    if (!isOn)
+    {
+        tS = std::chrono::high_resolution_clock::now();
+        cS = std::clock();
+        isOn = true;
+    }
+    else
+        throw std::runtime_error("Cannot start a running timer!\n");
+}
+
+/**
+ * @brief Stop the timer
+ */
+void Timer::stop()
+{
+    if (isOn)
+    {
+        tE = std::chrono::high_resolution_clock::now();
+        cE = std::clock();
+        isOn = false;
+    }
+    else
+        throw std::runtime_error("Cannot stop a non-running timer!\n");
+}
+
+/**
+ * @brief Access/create a given timer
+ */
+Timer &Timers::operator[](std::string const &name)
+{
+    return timers[name];
+}
+
+/**
+ * @brief Print the times of all timers
+ */
+void Timers::write(std::ostream &out) const
+{
+    out << std::fixed << std::setprecision(1);
+    out << std::setw(20) << std::left << "Timer"
+        << std::setw(15) << std::right << "Wall-clock (s)"
+        << std::setw(15) << std::right << "CPU (s)"
+        << std::endl;
+
+    for (auto const tm : timers)
+    {
+        out << std::setw(20) << std::left << tm.first
+            << std::setw(15) << std::right << tm.second.getWall()
+            << std::setw(15) << std::right << tm.second.getCpu()
+            << std::endl;
+    }
+}
diff --git a/fpm/src/fTimers.h b/fpm/src/fTimers.h
new file mode 100644
index 0000000..510df45
--- /dev/null
+++ b/fpm/src/fTimers.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2020 University of Liège
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FTIMERS_H
+#define FTIMERS_H
+
+#include "fpm.h"
+#include "wObject.h"
+#include <map>
+#include <chrono>
+
+namespace fpm
+{
+
+/**
+ * @brief Basic and portable timer
+ * @author Adrien Crovato
+ * @todo use clock_gettime for POSIX (https://en.cppreference.com/w/cpp/chrono/c/clock)?
+ */
+class FPM_API Timer
+{
+private:
+    bool isOn;                                         ///< status
+    std::chrono::high_resolution_clock::time_point tS; ///< start time (wall-clock)
+    std::chrono::high_resolution_clock::time_point tE; ///< stop time (wall-clock)
+    std::clock_t cS;                                   ///< start time (cpu)
+    std::clock_t cE;                                   ///< stop time (cpu)
+
+public:
+    Timer();
+    virtual ~Timer() {}
+
+    void reset();
+    void start();
+    void stop();
+    inline double getWall() const;
+    inline double getCpu() const;
+};
+
+#include "fTimers.inl"
+
+/**
+ * @brief Collection of timers
+ * @author Romain Boman, Adrien Crovato
+ */
+class FPM_API Timers : public fwk::wObject
+{
+private:
+    std::map<std::string, Timer> timers; ///< set of timers
+
+public:
+    Timers() : fwk::wObject() {}
+    virtual ~Timers() {}
+
+#ifndef SWIG
+    Timer &operator[](std::string const &name);
+    virtual void write(std::ostream &out) const override;
+#endif //SWIG
+};
+
+} // namespace fpm
+
+#endif //FTIMERS_H
diff --git a/fpm/src/fObject.cpp b/fpm/src/fTimers.inl
similarity index 60%
rename from fpm/src/fObject.cpp
rename to fpm/src/fTimers.inl
index 55e8ca7..1fae112 100644
--- a/fpm/src/fObject.cpp
+++ b/fpm/src/fTimers.inl
@@ -14,32 +14,18 @@
  * limitations under the License.
  */
 
-#include "fObject.h"
-
-namespace fpm
-{
-
-FPM_API std::ostream &
-operator<<(std::ostream &out, fObject const &obj)
-{
-    obj.write(out);
-    return out;
-}
-
-void fObject::write(std::ostream &out) const
-{
-}
-
-FPM_API std::ostream &
-operator<<(std::ostream &out, fSharedObject const &obj)
+/**
+ * @brief Compute the wall-clock (ellasped) time from "start" to "stop"
+ */
+inline double Timer::getWall() const
 {
-    obj.write(out);
-    return out;
+    return std::chrono::duration<double>(tE - tS).count();
 }
 
-void fSharedObject::write(std::ostream &out) const
+/**
+ * @brief Compute the cpu (user) time from "start" to "stop"
+ */
+inline double Timer::getCpu() const
 {
-}
-
-} // namespace fpm
-
+    return (cE - cS) / (double)CLOCKS_PER_SEC;
+}
\ No newline at end of file
diff --git a/fpm/src/fpm.h b/fpm/src/fpm.h
index e925020..e408ab5 100644
--- a/fpm/src/fpm.h
+++ b/fpm/src/fpm.h
@@ -29,26 +29,15 @@
 #define FPM_API
 #endif
 
-#ifdef _MSC_VER
-#if !defined(_CRT_SECURE_NO_WARNINGS)
-#define _CRT_SECURE_NO_WARNINGS 1
-#endif
-
-#pragma warning(disable : 4251) // DLL/templates non exportes
-#pragma warning(disable : 4275) // non - DLL-interface classkey 'identifier' used as base for DLL-interface classkey 'identifier'
-
-#define NOMINMAX // avoids the definition of "min/max" macro  in <minwindef.h> (which could shadow std::max)
-
-#endif //_MSC_VER
-
+#include "tbox.h"
 
 namespace fpm
 {
 
-class fObject;
+// utilities
+class fTimer;
+class fTimers;
 
 } // namespace fpm
 
-
 #endif //FPM_H
-
diff --git a/fpm/tests/basic.py b/fpm/tests/timers.py
similarity index 55%
rename from fpm/tests/basic.py
rename to fpm/tests/timers.py
index 6da436c..1c66613 100644
--- a/fpm/tests/basic.py
+++ b/fpm/tests/timers.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 
 # Copyright 2020 University of Liège
@@ -15,17 +15,38 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# basic test
+# Test the timers
+# Adrien Crovato
 
 import fpm
+import time
 from fwk.testing import *
 from fwk.coloring import ccolors
 
 def main():
-    a = 10.0
+    tms = fpm.Timers()
+    tms['1'].start()
+    time.sleep(0.3)
+    tms['1'].stop()
+    tms['2'].start()
+    tic = time.perf_counter()
+    dummy()
+    toc = time.perf_counter()
+    tms['2'].stop()
+    print(ccolors.ANSI_BLUE + 'PyTiming...' + ccolors.ANSI_RESET)
+    print(tms)
+
     print(ccolors.ANSI_BLUE + 'PyTesting...' + ccolors.ANSI_RESET)
     tests = CTests()
-    tests.add(CTest('test', a, 10., 1e-2))
+    tests.add(CTest('1w', tms['1'].getWall(), 0.3, 2e-2))
+    tests.add(CTest('1u', tms['1'].getCpu(), 0.0, 1e-2))
+    tests.add(CTest('2w', tms['2'].getWall(), toc - tic, 2e-2))
+    tests.add(CTest('2u', tms['2'].getCpu(), toc - tic, 2e-2))
+    tests.run()
+    
+def dummy():
+    for i in range(0, int(1e7)):
+        a = 1+1
 
 if __name__ == '__main__':
     main()
-- 
GitLab