From a588d14057bc916fb4d913b526d175da968744b8 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (o)" <j.wuttke@fz-juelich.de>
Date: Wed, 24 Aug 2016 15:15:18 +0200
Subject: [PATCH] ProgressHandler is now a member of Simulation.

---
 Core/Basics/ISingleton.h                      | 24 +++---
 Core/Computation/MainComputation.cpp          |  9 +-
 Core/Computation/MainComputation.h            | 22 ++---
 Core/Computation/ProgressHandler.cpp          | 40 +++++----
 Core/Computation/ProgressHandler.h            | 28 +++---
 Core/Computation/ProgressHandlerDWBA.cpp      | 52 ------------
 Core/Computation/ProgressHandlerDWBA.h        | 40 ---------
 Core/Lattice/Lattice.cpp                      | 10 ---
 Core/Lattice/Lattice.h                        |  4 +-
 .../IInterferenceFunctionStrategy.cpp         |  2 +-
 Core/Simulation/Simulation.cpp                | 14 +--
 Core/Simulation/Simulation.h                  |  7 +-
 GUI/coregui/Models/JobWorker.cpp              | 20 ++---
 GUI/coregui/Models/JobWorker.h                |  4 +-
 Tests/Functional/Core/CoreStandardTest.cpp    |  4 +-
 Tests/Functional/GUI/GUIStandardTest.cpp      |  4 +-
 .../PyCore/export/PyExportStandardTest.cpp    |  5 +-
 .../TestMachinery/IFunctionalTest.h           |  3 +-
 .../TestMachinery/IReferencedTest.h           |  5 +-
 .../TestMachinery/IStandardTest.cpp           | 15 ++--
 .../Functional/TestMachinery/IStandardTest.h  |  6 +-
 auto/Wrap/libBornAgainCore.py                 | 18 ++--
 auto/Wrap/libBornAgainCore_wrap.cpp           | 85 ++++++-------------
 cmake/bornagain/modules/SetupCoverage.cmake   |  1 +
 24 files changed, 135 insertions(+), 287 deletions(-)
 delete mode 100644 Core/Computation/ProgressHandlerDWBA.cpp
 delete mode 100644 Core/Computation/ProgressHandlerDWBA.h

diff --git a/Core/Basics/ISingleton.h b/Core/Basics/ISingleton.h
index ce15f6341df..2fbc1861c2a 100644
--- a/Core/Basics/ISingleton.h
+++ b/Core/Basics/ISingleton.h
@@ -18,42 +18,40 @@
 
 #include <iostream>
 #include <mutex>
-#include <stdexcept> // need overlooked by g++ 5.4
+#include <stdexcept>
 
-//! @class ISingleton
+//! Base class for singletons.
 //! @ingroup tools_internal
-//! @brief Singleton pattern.
 
 template <class T>
 class ISingleton
 {
 public:
-    static T& instance()
-    {
+    static T& instance() {
         static std::mutex single_mutex;
         std::unique_lock<std::mutex> single_lock( single_mutex );
         if( !m_instance) {
             if( m_destroyed )
-                throw std::runtime_error("Bug in ISingleton: object was destructed!");
+                // In BornAgain, an ISingleton is deleted when and only when the application
+                // terminates. Therefore there is no point in re-creating a deleted ISingleton.
+                // To be 110% sure, we explicitly forbid re-creation.
+                throw std::runtime_error("Invalid attempt to re-create a deleted ISingleton");
             static T theInstance;
             m_instance = &theInstance;
         }
-        return *m_instance;
-    }
+        return *m_instance; }
 
 protected:
     ISingleton(){}
-    virtual ~ISingleton()
-    {
+    virtual ~ISingleton() {
         m_instance = nullptr;
-        m_destroyed = true;
-    }
+        m_destroyed = true; }
 
 private:
     ISingleton(const ISingleton&) = delete;
     ISingleton& operator=(const ISingleton&) = delete;
     static T* m_instance;
-    static bool m_destroyed;
+    static bool m_destroyed; //!< to detect re-creation
 };
 
 // for templated classes, initializations go into the .h file:
diff --git a/Core/Computation/MainComputation.cpp b/Core/Computation/MainComputation.cpp
index 138d1d384b1..3771c1f1b45 100644
--- a/Core/Computation/MainComputation.cpp
+++ b/Core/Computation/MainComputation.cpp
@@ -26,7 +26,6 @@
 #include "RoughMultiLayerComputation.h"
 #include "ScalarSpecularInfoMap.h"
 #include "ProgressHandler.h"
-#include "ProgressHandlerDWBA.h"
 #include "SimulationElement.h"
 #include "SpecularMagnetic.h"
 #include "SpecularMatrix.h"
@@ -37,10 +36,11 @@
 MainComputation::MainComputation(
     const MultiLayer* p_multi_layer,
     const SimulationOptions& options,
-    ProgressHandler* progress,
+    ProgressHandler& progress,
     const std::vector<SimulationElement>::iterator& begin_it,
     const std::vector<SimulationElement>::iterator& end_it)
     : m_sim_options(options)
+    , m_progress(&progress)
     , mp_roughness_computation(nullptr)
 {
     mp_multi_layer = p_multi_layer->clone();
@@ -49,10 +49,6 @@ MainComputation::MainComputation(
     m_begin_it = begin_it;
     m_end_it = end_it;
 
-    // initialising call backs
-    if (progress)
-        m_progress.setCallback( [&] (int n) {return progress->update(n);} );
-
     for (size_t i=0; i<mp_multi_layer->getNumberOfLayers(); ++i) {
         m_layer_computation.push_back({});
         for (size_t j=0; j<mp_multi_layer->getLayer(i)->getNumberOfLayouts(); ++j)
@@ -115,6 +111,7 @@ void MainComputation::runProtected()
         mp_roughness_computation->eval(layer_elements.begin(), layer_elements.end());
         addElementsWithWeight(layer_elements.begin(), layer_elements.end(), m_begin_it, 1.0);
     }
+    m_progress->incrementDone(1);
 }
 
 void MainComputation::collectRTCoefficientsScalar()
diff --git a/Core/Computation/MainComputation.h b/Core/Computation/MainComputation.h
index e99ab7e3426..3c4bea57b06 100644
--- a/Core/Computation/MainComputation.h
+++ b/Core/Computation/MainComputation.h
@@ -19,7 +19,6 @@
 #include "ComputationOutcome.h"
 #include "Complex.h"
 #include "INoncopyable.h"
-#include "ProgressHandlerDWBA.h"
 #include "SimulationOptions.h"
 #include <vector>
 
@@ -29,7 +28,11 @@ class RoughMultiLayerComputation;
 class ProgressHandler;
 class SimulationElement;
 
-//! Performs a DWBA calculation with given sample and simulation parameters
+//! Performs a single-threaded DWBA computation with given sample and simulation parameters,
+//! for a given span of detector bins.
+//!
+//! Controlled by the multi-threading machinery in Simulation::runSingleSimulation().
+//!
 //! @ingroup algorithms_internal
 
 class BA_CORE_API_ MainComputation : public INoncopyable
@@ -38,7 +41,7 @@ public:
     MainComputation(
         const MultiLayer* p_multi_layer,
         const SimulationOptions& options,
-        ProgressHandler* progress,
+        ProgressHandler& progress,
         const std::vector<SimulationElement>::iterator& begin_it,
         const std::vector<SimulationElement>::iterator& end_it);
     ~MainComputation();
@@ -55,16 +58,15 @@ private:
     void collectRTCoefficientsScalar();
     void collectRTCoefficientsMatrix();
 
-    //! Iterators that defines the sequence of elements that this simulation will work on
-    std::vector<SimulationElement>::iterator m_begin_it, m_end_it;
-
+    MultiLayer* mp_multi_layer;
     SimulationOptions m_sim_options;
+    ProgressHandler* m_progress;
+    //! these iterators define the span of detector bins this simulation will work on
+    std::vector<SimulationElement>::iterator m_begin_it, m_end_it;
 
-    ProgressHandlerDWBA m_progress;
-
-    std::vector<std::vector<DecoratedLayerComputation*>> m_layer_computation;
-    MultiLayer* mp_multi_layer;
     RoughMultiLayerComputation* mp_roughness_computation;
+    std::vector<std::vector<DecoratedLayerComputation*>> m_layer_computation;
+
     ComputationOutcome m_outcome;
 };
 
diff --git a/Core/Computation/ProgressHandler.cpp b/Core/Computation/ProgressHandler.cpp
index c7fb3f30130..385089a7095 100644
--- a/Core/Computation/ProgressHandler.cpp
+++ b/Core/Computation/ProgressHandler.cpp
@@ -18,33 +18,31 @@
 #include "LayerInterface.h"
 #include "MultiLayer.h"
 #include <mutex>
+#include <stdexcept>
 
-ProgressHandler::ProgressHandler()
-    : m_callback(nullptr)
-    , m_completed_nticks(0)
-    , m_expected_nticks(0)
-    , m_percentage_done(0)
-{}
+void ProgressHandler::subscribe(ProgressHandler::Callback_t inform)
+{
+    if (m_inform)
+        throw std::runtime_error("Invalid call of ProgressHandler::subscribe: "
+                                 "currently, no more than one subscriber is allowed");
+    m_inform = inform;
+}
 
-//! Collects number of ticks processed by different Computation's.
-//! Calculates general progress and inform GUI if progress has changed.
-//! Return flag is obtained from GUI and transferred to Computation to ask
-//! them to stop calculations.
-bool ProgressHandler::update(size_t ticks_done)
+//! Increments number of completed computation steps (ticks).
+//! Performs callback (method m_inform) to inform the subscriber about
+//! the state of the computation and to obtain as return value a flag
+//! that indicates whether to continue the computation. Returns the
+//! value of that flag to request the owner to terminate.
+bool ProgressHandler::incrementDone(size_t ticks_done)
 {
     static std::mutex single_mutex;
     std::unique_lock<std::mutex> single_lock( single_mutex );
 
-    // this flag is to inform Simulation that GUI wants it to be terminated
-    bool continue_calculations(true);
-
     m_completed_nticks += ticks_done;
+    if (m_completed_nticks > m_expected_nticks)
+        m_expected_nticks = m_completed_nticks+1;
 
-    m_percentage_done = int(100.*m_completed_nticks/m_expected_nticks);
-    //std::cout << "ProgressHandler::update done" << ticks_done << " of " << m_expected_nticks
-    //         << " => progress:" << progress << std::endl;
-    if(m_callback)
-        continue_calculations = m_callback(m_percentage_done); // report to gui
-
-    return continue_calculations;
+    if(!m_inform)
+        return true;
+    return m_inform(percentage_done()); // report to subscriber, and get continuation flag
 }
diff --git a/Core/Computation/ProgressHandler.h b/Core/Computation/ProgressHandler.h
index 3e8a3ae681a..da16437298f 100644
--- a/Core/Computation/ProgressHandler.h
+++ b/Core/Computation/ProgressHandler.h
@@ -22,29 +22,35 @@
 
 class MultiLayer;
 
-//! Provides the functionality to calculate the progress of running simulation and report it to GUI.
-//!
-//! Thread safe to be used from Computation.
+//! Maintains information about progress of a computation.
+//! Owner is the computation, which periodically calls the thread-safe function incrementDone(..).
+//! An application (GUI or script) may subscribe(..) to be informed about progress.
+//! It is then periodically called back by inform(..).
+//! The return value of inform(..) can be used to request termination of the computation.
 //!
 //! @ingroup algorithms_internal
 
-class BA_CORE_API_ ProgressHandler : public INoncopyable
+class BA_CORE_API_ ProgressHandler
 {
 public:
     typedef std::function<bool(size_t)> Callback_t;
 
-    ProgressHandler();
-
-    void setCallback(ProgressHandler::Callback_t callback) { m_callback = callback; }
+    ProgressHandler() : m_inform(nullptr), m_expected_nticks(0), m_completed_nticks(0) {}
+    ProgressHandler(const ProgressHandler& other)
+        : m_inform(nullptr) // not clear whether we want to copy subscriptions
+        , m_expected_nticks(other.m_expected_nticks)
+        , m_completed_nticks(other.m_completed_nticks) {}
+    void subscribe(ProgressHandler::Callback_t callback);
+    void reset() { m_completed_nticks = 0; }
     void setExpectedNTicks(size_t n) { m_expected_nticks = n; }
+    bool incrementDone(size_t ticks_done);
 
-    bool update(size_t ticks_done);
+    int  percentage_done() const { return 100.*m_completed_nticks/m_expected_nticks; }
 
 private:
-    ProgressHandler::Callback_t m_callback;
-    size_t m_completed_nticks;
+    ProgressHandler::Callback_t m_inform;
     size_t m_expected_nticks;
-    int m_percentage_done;
+    size_t m_completed_nticks;
 };
 
 #endif // PROGRESSHANDLER_H
diff --git a/Core/Computation/ProgressHandlerDWBA.cpp b/Core/Computation/ProgressHandlerDWBA.cpp
deleted file mode 100644
index ac3a1d3fd13..00000000000
--- a/Core/Computation/ProgressHandlerDWBA.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-// ************************************************************************** //
-//
-//  BornAgain: simulate and fit scattering at grazing incidence
-//
-//! @file      Core/Computation/ProgressHandlerDWBA.cpp
-//! @brief     Implements class ProgressHandlerDWBA.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2015
-//! @authors   Scientific Computing Group at MLZ Garching
-//! @authors   C. Durniak, M. Ganeva, G. Pospelov, W. Van Herck, J. Wuttke
-//
-// ************************************************************************** //
-
-#include "ProgressHandlerDWBA.h"
-
-ProgressHandlerDWBA::ProgressHandlerDWBA()
-    : m_nitems(0)
-    , m_nitems_total(0)
-    , m_report_every_nth(100)
-{}
-
-
-//! Method increments number of items processed.
-//! Every n'th processed item the Simulation is informed via thread safe callback.
-//! Return flag false is used to inform DWBSimulation to interrupt calculations.
-bool ProgressHandlerDWBA::update()
-{
-    bool continue_calculations(true);
-    if(!m_callback)
-        return continue_calculations;
-
-    m_nitems_total++;
-    m_nitems++;
-    if(m_nitems >= m_report_every_nth) {
-        continue_calculations = m_callback(m_nitems); // report to the Simulation
-        m_nitems=0;
-    }
-    return continue_calculations;
-}
-
-
-//! finalize report to the simulation
-bool ProgressHandlerDWBA::finished()
-{
-    if(m_callback) {
-        m_callback(m_nitems); // report to the Simulation
-        m_nitems = 0;
-    }
-    return true;
-}
diff --git a/Core/Computation/ProgressHandlerDWBA.h b/Core/Computation/ProgressHandlerDWBA.h
deleted file mode 100644
index 44f1ff875e7..00000000000
--- a/Core/Computation/ProgressHandlerDWBA.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// ************************************************************************** //
-//
-//  BornAgain: simulate and fit scattering at grazing incidence
-//
-//! @file      Core/Computation/ProgressHandlerDWBA.h
-//! @brief     Defines class ProgressHandlerDWBA.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2015
-//! @authors   Scientific Computing Group at MLZ Garching
-//! @authors   C. Durniak, M. Ganeva, G. Pospelov, W. Van Herck, J. Wuttke
-//
-// ************************************************************************** //
-
-#ifndef PROGRESSHANDLERDWBA_H
-#define PROGRESSHANDLERDWBA_H
-
-#include "ProgressHandler.h"
-
-//! Holds number of items processed by Computation,
-//! and informs Simulation every time n items have been processed.
-//! @ingroup algorithms_internal
-
-class ProgressHandlerDWBA
-{
-public:
-    ProgressHandlerDWBA();
-    void setCallback(ProgressHandler::Callback_t callback) { m_callback = callback; }
-    ProgressHandler::Callback_t getCallback() const { return m_callback; }
-    bool update();
-    bool finished();
-private:
-    ProgressHandler::Callback_t m_callback;
-    long m_nitems;
-    long m_nitems_total;
-    long m_report_every_nth;
-};
-
-#endif // PROGRESSHANDLERDWBA_H
diff --git a/Core/Lattice/Lattice.cpp b/Core/Lattice/Lattice.cpp
index ceca2754dbf..b11284cfe00 100644
--- a/Core/Lattice/Lattice.cpp
+++ b/Core/Lattice/Lattice.cpp
@@ -19,21 +19,12 @@
 #include "Transform3D.h"
 #include <gsl/gsl_linalg.h>
 
-Lattice::Lattice()
-: mp_selection_rule(0)
-, m_cache_ok(false)
-, m_is_zero(true)
-{
-    initialize();
-}
-
 Lattice::Lattice(const kvector_t a1, const kvector_t a2, const kvector_t a3)
 : mp_selection_rule(0)
 , m_a1(a1)
 , m_a2(a2)
 , m_a3(a3)
 , m_cache_ok(false)
-, m_is_zero(false)
 {
     initialize();
 }
@@ -44,7 +35,6 @@ Lattice::Lattice(const Lattice& lattice)
 , m_a2(lattice.m_a2)
 , m_a3(lattice.m_a3)
 , m_cache_ok(false)
-, m_is_zero(false)
 {
     initialize();
     if( lattice.mp_selection_rule ) setSelectionRule(*lattice.mp_selection_rule);
diff --git a/Core/Lattice/Lattice.h b/Core/Lattice/Lattice.h
index 7302a60f0b0..493e726e11a 100644
--- a/Core/Lattice/Lattice.h
+++ b/Core/Lattice/Lattice.h
@@ -28,7 +28,7 @@ class Transform3D;
 class BA_CORE_API_ Lattice
 {
 public:
-    Lattice();
+    Lattice() =delete;
     Lattice(const kvector_t a1, const kvector_t a2, const kvector_t a3);
     Lattice(const Lattice& lattice);
     ~Lattice();
@@ -87,7 +87,7 @@ private:
     kvector_t m_a1, m_a2, m_a3; //!< Basis vectors in real space
     mutable kvector_t m_b1, m_b2, m_b3; //!< Cache of basis vectors in reciprocal space
     //! Boolean indicating if the reciprocal vectors are already initialized in the cache
-    mutable bool m_cache_ok, m_is_zero;
+    mutable bool m_cache_ok;
 };
 
 #endif // LATTICE_H
diff --git a/Core/Multilayer/IInterferenceFunctionStrategy.cpp b/Core/Multilayer/IInterferenceFunctionStrategy.cpp
index 71aabfcfe1f..229fe229699 100644
--- a/Core/Multilayer/IInterferenceFunctionStrategy.cpp
+++ b/Core/Multilayer/IInterferenceFunctionStrategy.cpp
@@ -157,6 +157,6 @@ double IInterferenceFunctionStrategy::evaluate_for_fixed_angles_pol(
     SimulationElement* pars = static_cast<SimulationElement*>(params);
 
     SimulationElement sim_element(*pars, par0, par1);
-    calculateFormFactorLists(sim_element);
+    calculateFormFactorListPol(sim_element);
     return pars->getIntegrationFactor(par0, par1) * evaluateForMatrixList(sim_element, m_ff_pol);
 }
diff --git a/Core/Simulation/Simulation.cpp b/Core/Simulation/Simulation.cpp
index fa9b73280f4..749e56e6f6b 100644
--- a/Core/Simulation/Simulation.cpp
+++ b/Core/Simulation/Simulation.cpp
@@ -27,7 +27,6 @@
 #include <thread>
 
 Simulation::Simulation()
-    : m_progress(nullptr)
 {}
 
 Simulation::~Simulation() {} // forward class declaration prevents move to .h
@@ -61,15 +60,18 @@ void Simulation::prepareSimulation()
 //! Run simulation with possible averaging over parameter distributions
 void Simulation::runSimulation()
 {
+    updateSample();
+    if (!mP_sample)
+        throw Exceptions::NullPointerException("Simulation::runSimulation() -> Error! No sample.");
+
     prepareSimulation();
 
     size_t param_combinations = m_distribution_handler.getTotalNumberOfSamples();
 
-    if (m_progress) {
-        int prefac = ( mP_sample->totalNofLayouts()>0 ? 1 : 0 )
-            + ( mP_sample->hasRoughness() ? 1 : 0 );
-        m_progress->setExpectedNTicks(prefac*param_combinations*getNumberOfSimulationElements());
-    }
+    m_progress.reset();
+    int prefac = ( mP_sample->totalNofLayouts()>0 ? 1 : 0 )
+        + ( mP_sample->hasRoughness() ? 1 : 0 );
+    m_progress.setExpectedNTicks(prefac*param_combinations*getNumberOfSimulationElements());
 
     // no averaging needed:
     if (param_combinations == 1) {
diff --git a/Core/Simulation/Simulation.h b/Core/Simulation/Simulation.h
index ae86809d6cd..ab13bac6bcd 100644
--- a/Core/Simulation/Simulation.h
+++ b/Core/Simulation/Simulation.h
@@ -81,15 +81,14 @@ public:
 
     const DistributionHandler& getDistributionHandler() const;
 
-    //! sets progress handler (used by GUI)
-    void setProgressHandler(ProgressHandler* progress) { m_progress = progress; }
-
     //unused friend class OMPISimulation;
 
     void setOptions(const SimulationOptions& options) { m_options = options; }
     const SimulationOptions& getOptions() const { return m_options; }
     SimulationOptions& getOptions() { return m_options; }
 
+    ProgressHandler& progressHandler() { return m_progress; }
+
 protected:
     Simulation(const Simulation& other);
 
@@ -123,7 +122,7 @@ protected:
     std::shared_ptr<IMultiLayerBuilder> mp_sample_builder;
     SimulationOptions m_options;
     DistributionHandler m_distribution_handler;
-    ProgressHandler* m_progress;
+    ProgressHandler m_progress;
     std::vector<SimulationElement> m_sim_elements;
 
 private:
diff --git a/GUI/coregui/Models/JobWorker.cpp b/GUI/coregui/Models/JobWorker.cpp
index 58dff1d6c3d..75d306405a7 100644
--- a/GUI/coregui/Models/JobWorker.cpp
+++ b/GUI/coregui/Models/JobWorker.cpp
@@ -32,13 +32,6 @@ JobWorker::JobWorker(QString identifier, GISASSimulation *simulation)
 
 }
 
-int JobWorker::getProgress() const
-{
-    // sometimes simulation underestimate the number of iterations required
-    // and progress can be greater than 100
-    return m_percentage_done < 100 ? m_percentage_done : 100;
-}
-
 void JobWorker::start()
 {
     qDebug() << "JobRunner::start() " << m_simulation;
@@ -47,11 +40,9 @@ void JobWorker::start()
     emit started();
 
     if(m_simulation) {
-        std::unique_ptr<ProgressHandler> progressHandler(new ProgressHandler());
-        ProgressHandler::Callback_t callback = [this] (int percentage_done) {
-            return simulationProgressCallback(percentage_done); };
-        progressHandler->setCallback(callback);
-        m_simulation->setProgressHandler(progressHandler.get());
+        m_simulation->progressHandler().subscribe(
+            [this] (int percentage_done) {
+                return calledbackByProgressHandler(percentage_done); } );
 
         m_job_status = Constants::STATUS_RUNNING;
 
@@ -84,8 +75,9 @@ void JobWorker::start()
     emit finished();
 }
 
-//! function which is called by the simulation to report its progress
-bool JobWorker::simulationProgressCallback(int percentage_done)
+//! Informs us about progress of the simulation. Returns true if we want to continue the simulation.
+//! To be registered as callback function via ProgressHandler::subscribe().
+bool JobWorker::calledbackByProgressHandler(int percentage_done)
 {
     if (percentage_done > m_percentage_done) {
         m_percentage_done = percentage_done;
diff --git a/GUI/coregui/Models/JobWorker.h b/GUI/coregui/Models/JobWorker.h
index 01392462f4e..d11e29cb6bb 100644
--- a/GUI/coregui/Models/JobWorker.h
+++ b/GUI/coregui/Models/JobWorker.h
@@ -34,9 +34,9 @@ public:
     QString getIdentifier() const { return m_identifier; }
     void setIdentifier(QString identifier) { m_identifier = identifier; }
 
-    int getProgress() const;
+    int getProgress() const { return m_percentage_done; }
 
-    bool simulationProgressCallback(int);
+    bool calledbackByProgressHandler(int);
 
     bool isTerminated() { return m_terminate_request_flag; }
 
diff --git a/Tests/Functional/Core/CoreStandardTest.cpp b/Tests/Functional/Core/CoreStandardTest.cpp
index a3873ee17f8..7dca9c2bc79 100644
--- a/Tests/Functional/Core/CoreStandardTest.cpp
+++ b/Tests/Functional/Core/CoreStandardTest.cpp
@@ -21,8 +21,8 @@ class CoreStandardTest : public IStandardTest
 {
 public:
     CoreStandardTest() : IStandardTest("CoreStandardTest") {}
-    IFunctionalTest* getTest() const { return new CoreTest(
-            getName(), getTestDescription(), getSimulation(), getTestThreshold() ); }
+    std::unique_ptr<IFunctionalTest> getTest() const { return std::unique_ptr<IFunctionalTest>
+            (new CoreTest(getName(), getTestDescription(), getSimulation(), getTestThreshold())); }
 };
 
 //! Runs CoreTest on a standard simulation indicated by argv[1].
diff --git a/Tests/Functional/GUI/GUIStandardTest.cpp b/Tests/Functional/GUI/GUIStandardTest.cpp
index 579866ef0c5..5b9cee985d1 100644
--- a/Tests/Functional/GUI/GUIStandardTest.cpp
+++ b/Tests/Functional/GUI/GUIStandardTest.cpp
@@ -22,8 +22,8 @@ class GUIStandardTest : public IStandardTest
 {
 public:
     GUIStandardTest() : IStandardTest("GUIStandardTest") {}
-    IFunctionalTest* getTest() const { return new GUITest(
-            getName(), getTestDescription(), getSimulation(), getTestThreshold()); }
+    std::unique_ptr<IFunctionalTest> getTest() const { return std::unique_ptr<IFunctionalTest>
+            (new GUITest(getName(), getTestDescription(), getSimulation(), getTestThreshold())); }
 };
 
 //! Runs GUITest on a standard simulation indicated by argv[1].
diff --git a/Tests/Functional/PyCore/export/PyExportStandardTest.cpp b/Tests/Functional/PyCore/export/PyExportStandardTest.cpp
index aae3c878d93..8e25a6e43fc 100644
--- a/Tests/Functional/PyCore/export/PyExportStandardTest.cpp
+++ b/Tests/Functional/PyCore/export/PyExportStandardTest.cpp
@@ -21,8 +21,9 @@ class PyExportStandardTest : public IStandardTest
 {
 public:
     PyExportStandardTest() : IStandardTest("PyExport") {}
-    IFunctionalTest* getTest() const { return new PyExportTest(
-            getName(), getTestDescription(), getSimulation(), getTestThreshold()); }
+    std::unique_ptr<IFunctionalTest> getTest() const { return std::unique_ptr<IFunctionalTest>
+            (new PyExportTest(
+                getName(), getTestDescription(), getSimulation(), getTestThreshold())); }
 };
 
 //! Runs PyExportTest on a standard simulation indicated by argv[1].
diff --git a/Tests/Functional/TestMachinery/IFunctionalTest.h b/Tests/Functional/TestMachinery/IFunctionalTest.h
index 35f79c6e28e..6505504681a 100644
--- a/Tests/Functional/TestMachinery/IFunctionalTest.h
+++ b/Tests/Functional/TestMachinery/IFunctionalTest.h
@@ -19,9 +19,8 @@
 #include "INamed.h"
 #include <map>
 
+//! Base class for all functional tests.
 //! @class IFunctionalTest
-//! @ingroup standard_samples
-//! @brief Base class for all functional tests.
 
 class IFunctionalTest : public INamed
 {
diff --git a/Tests/Functional/TestMachinery/IReferencedTest.h b/Tests/Functional/TestMachinery/IReferencedTest.h
index 0ad31b69e98..26d1b899a0f 100644
--- a/Tests/Functional/TestMachinery/IReferencedTest.h
+++ b/Tests/Functional/TestMachinery/IReferencedTest.h
@@ -21,9 +21,8 @@
 #include <map>
 #include <string>
 
-//! @class IReferencedTest
+//! Base class for tests that compare results with reference data.
 //! @ingroup standard_samples
-//! @brief Base class for tests that compare results with reference data.
 
 class IReferencedTest : public IFunctionalTest
 {
@@ -33,7 +32,7 @@ public:
         : IFunctionalTest(name, description), m_threshold(threshold) {}
     virtual ~IReferencedTest() {}
 
-    bool runTest() = 0;
+    bool runTest() =0;
 
 protected:
     double m_threshold;
diff --git a/Tests/Functional/TestMachinery/IStandardTest.cpp b/Tests/Functional/TestMachinery/IStandardTest.cpp
index f30d4477e46..86cc02619bd 100644
--- a/Tests/Functional/TestMachinery/IStandardTest.cpp
+++ b/Tests/Functional/TestMachinery/IStandardTest.cpp
@@ -47,8 +47,7 @@ bool IStandardTest::execute(int argc, char** argv) {
 bool IStandardTest::execute_onetest()
 {
     setName( m_info->m_test_name );
-    IFunctionalTest* test( getTest() );
-    return test->runTest();
+    return getTest()->runTest();
 }
 
 //! Runs all available subtests, and returns true if all succeed
@@ -73,13 +72,10 @@ bool IStandardTest::execute_subtests()
     for (size_t i = 0; i < n_subtests; ++i) {
         setName( m_info->m_test_name + "_" + subtest_names[i] );
         m_subtest_item = subtest_registry->getItem(subtest_names[i]);
-        IFunctionalTest* subtest( getTest() );
         std::cout << "IStandardTest::execute() -> " << getName()
                   << " " << i+1 << "/" << n_subtests << " (" << subtest_names[i] << ")\n";
-        if(!subtest->runTest()) {
+        if(!getTest()->runTest())
             ++number_of_failed_tests;
-            delete subtest;
-        }
     }
     delete subtest_registry;
 
@@ -99,13 +95,12 @@ double IStandardTest::getTestThreshold() const { return m_info->m_threshold; }
 
 GISASSimulation* IStandardTest::getSimulation() const
 {
-    SimulationFactory sim_registry;
-    GISASSimulation* result = sim_registry.createItem(m_info->m_simulation_name);
-    SampleBuilderFactory sample_factory;
     std::shared_ptr<IMultiLayerBuilder> sample_builder(
-        sample_factory.createItem(m_info->m_sample_builder_name) );
+        SampleBuilderFactory().createItem(m_info->m_sample_builder_name) );
     if(m_subtest_item)
         sample_builder->set_subtest(m_subtest_item);
+
+    GISASSimulation* result = SimulationFactory().createItem(m_info->m_simulation_name);
     result->setSampleBuilder(sample_builder);
     return result;
 }
diff --git a/Tests/Functional/TestMachinery/IStandardTest.h b/Tests/Functional/TestMachinery/IStandardTest.h
index 0b911b04d6c..cfdc51e258a 100644
--- a/Tests/Functional/TestMachinery/IStandardTest.h
+++ b/Tests/Functional/TestMachinery/IStandardTest.h
@@ -17,14 +17,15 @@
 #define ISTANDARDTEST_H
 
 #include "INamed.h"
+#include <memory>
 
 class GISASSimulation;
 class IFunctionalTest;
 class SimulationInfo;
 class IParameterized;
 
+//! Base class for Core/PyCore/GUI tests that involve standard simulations.
 //! @class IStandardTest
-//! @brief Base class for Core/PyCore/GUI tests that involve standard simulations.
 
 //! For Foo in {Core, PyCore, GUI}, the functional test mechanism is as follows:
 //!
@@ -60,7 +61,8 @@ public:
 
     bool execute(int argc, char** argv);
 
-    virtual IFunctionalTest* getTest() const = 0; //!< overloaded in (Core|Py|GUI)Suite.cpp
+    //! Returns a specific standard functional test kernel like CoreTest, PyExportTest, GUITest.
+    virtual std::unique_ptr<IFunctionalTest> getTest() const = 0;
 
 protected:
     virtual GISASSimulation* getSimulation() const;
diff --git a/auto/Wrap/libBornAgainCore.py b/auto/Wrap/libBornAgainCore.py
index fc39af307ba..7f0e38eabd1 100644
--- a/auto/Wrap/libBornAgainCore.py
+++ b/auto/Wrap/libBornAgainCore.py
@@ -14927,18 +14927,6 @@ class Simulation(ICloneable, IParameterized):
         return _libBornAgainCore.Simulation_getDistributionHandler(self)
 
 
-    def setProgressHandler(self, progress):
-        """
-        setProgressHandler(Simulation self, ProgressHandler * progress)
-
-        void Simulation::setProgressHandler(ProgressHandler *progress)
-
-        sets progress handler (used by GUI) 
-
-        """
-        return _libBornAgainCore.Simulation_setProgressHandler(self, progress)
-
-
     def setOptions(self, options):
         """
         setOptions(Simulation self, SimulationOptions options)
@@ -14959,6 +14947,11 @@ class Simulation(ICloneable, IParameterized):
         """
         return _libBornAgainCore.Simulation_getOptions(self, *args)
 
+
+    def progressHandler(self):
+        """progressHandler(Simulation self) -> ProgressHandler &"""
+        return _libBornAgainCore.Simulation_progressHandler(self)
+
 Simulation_swigregister = _libBornAgainCore.Simulation_swigregister
 Simulation_swigregister(Simulation)
 
@@ -19586,7 +19579,6 @@ class Lattice(_object):
 
     def __init__(self, *args):
         """
-        __init__(Lattice self) -> Lattice
         __init__(Lattice self, kvector_t a1, kvector_t a2, kvector_t a3) -> Lattice
         __init__(Lattice self, Lattice lattice) -> Lattice
 
diff --git a/auto/Wrap/libBornAgainCore_wrap.cpp b/auto/Wrap/libBornAgainCore_wrap.cpp
index 8a030750383..897712410c3 100644
--- a/auto/Wrap/libBornAgainCore_wrap.cpp
+++ b/auto/Wrap/libBornAgainCore_wrap.cpp
@@ -68747,36 +68747,6 @@ fail:
 }
 
 
-SWIGINTERN PyObject *_wrap_Simulation_setProgressHandler(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
-  PyObject *resultobj = 0;
-  Simulation *arg1 = (Simulation *) 0 ;
-  ProgressHandler *arg2 = (ProgressHandler *) 0 ;
-  void *argp1 = 0 ;
-  int res1 = 0 ;
-  void *argp2 = 0 ;
-  int res2 = 0 ;
-  PyObject * obj0 = 0 ;
-  PyObject * obj1 = 0 ;
-  
-  if (!PyArg_ParseTuple(args,(char *)"OO:Simulation_setProgressHandler",&obj0,&obj1)) SWIG_fail;
-  res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_Simulation, 0 |  0 );
-  if (!SWIG_IsOK(res1)) {
-    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Simulation_setProgressHandler" "', argument " "1"" of type '" "Simulation *""'"); 
-  }
-  arg1 = reinterpret_cast< Simulation * >(argp1);
-  res2 = SWIG_ConvertPtr(obj1, &argp2,SWIGTYPE_p_ProgressHandler, 0 |  0 );
-  if (!SWIG_IsOK(res2)) {
-    SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Simulation_setProgressHandler" "', argument " "2"" of type '" "ProgressHandler *""'"); 
-  }
-  arg2 = reinterpret_cast< ProgressHandler * >(argp2);
-  (arg1)->setProgressHandler(arg2);
-  resultobj = SWIG_Py_Void();
-  return resultobj;
-fail:
-  return NULL;
-}
-
-
 SWIGINTERN PyObject *_wrap_Simulation_setOptions(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
   PyObject *resultobj = 0;
   Simulation *arg1 = (Simulation *) 0 ;
@@ -68894,6 +68864,28 @@ fail:
 }
 
 
+SWIGINTERN PyObject *_wrap_Simulation_progressHandler(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  Simulation *arg1 = (Simulation *) 0 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  PyObject * obj0 = 0 ;
+  ProgressHandler *result = 0 ;
+  
+  if (!PyArg_ParseTuple(args,(char *)"O:Simulation_progressHandler",&obj0)) SWIG_fail;
+  res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_Simulation, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Simulation_progressHandler" "', argument " "1"" of type '" "Simulation *""'"); 
+  }
+  arg1 = reinterpret_cast< Simulation * >(argp1);
+  result = (ProgressHandler *) &(arg1)->progressHandler();
+  resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ProgressHandler, 0 |  0 );
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
 SWIGINTERN PyObject *Simulation_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
   PyObject *obj;
   if (!PyArg_ParseTuple(args,(char*)"O:swigregister", &obj)) return NULL;
@@ -83673,19 +83665,6 @@ SWIGINTERN PyObject *IsGISAXSDetector_swigregister(PyObject *SWIGUNUSEDPARM(self
 }
 
 SWIGINTERN PyObject *_wrap_new_Lattice__SWIG_0(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
-  PyObject *resultobj = 0;
-  Lattice *result = 0 ;
-  
-  if (!PyArg_ParseTuple(args,(char *)":new_Lattice")) SWIG_fail;
-  result = (Lattice *)new Lattice();
-  resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Lattice, SWIG_POINTER_NEW |  0 );
-  return resultobj;
-fail:
-  return NULL;
-}
-
-
-SWIGINTERN PyObject *_wrap_new_Lattice__SWIG_1(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
   PyObject *resultobj = 0;
   kvector_t arg1 ;
   kvector_t arg2 ;
@@ -83749,7 +83728,7 @@ fail:
 }
 
 
-SWIGINTERN PyObject *_wrap_new_Lattice__SWIG_2(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+SWIGINTERN PyObject *_wrap_new_Lattice__SWIG_1(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
   PyObject *resultobj = 0;
   Lattice *arg1 = 0 ;
   void *argp1 = 0 ;
@@ -83786,15 +83765,12 @@ SWIGINTERN PyObject *_wrap_new_Lattice(PyObject *self, PyObject *args) {
   for (ii = 0; (ii < 3) && (ii < argc); ii++) {
     argv[ii] = PyTuple_GET_ITEM(args,ii);
   }
-  if (argc == 0) {
-    return _wrap_new_Lattice__SWIG_0(self, args);
-  }
   if (argc == 1) {
     int _v;
     int res = SWIG_ConvertPtr(argv[0], 0, SWIGTYPE_p_Lattice, 0);
     _v = SWIG_CheckState(res);
     if (_v) {
-      return _wrap_new_Lattice__SWIG_2(self, args);
+      return _wrap_new_Lattice__SWIG_1(self, args);
     }
   }
   if (argc == 3) {
@@ -83808,7 +83784,7 @@ SWIGINTERN PyObject *_wrap_new_Lattice(PyObject *self, PyObject *args) {
         int res = SWIG_ConvertPtr(argv[2], 0, SWIGTYPE_p_BasicVector3DT_double_t, 0);
         _v = SWIG_CheckState(res);
         if (_v) {
-          return _wrap_new_Lattice__SWIG_1(self, args);
+          return _wrap_new_Lattice__SWIG_0(self, args);
         }
       }
     }
@@ -83817,7 +83793,6 @@ SWIGINTERN PyObject *_wrap_new_Lattice(PyObject *self, PyObject *args) {
 fail:
   SWIG_SetErrorMsg(PyExc_NotImplementedError,"Wrong number or type of arguments for overloaded function 'new_Lattice'.\n"
     "  Possible C/C++ prototypes are:\n"
-    "    Lattice::Lattice()\n"
     "    Lattice::Lattice(kvector_t const,kvector_t const,kvector_t const)\n"
     "    Lattice::Lattice(Lattice const &)\n");
   return 0;
@@ -107616,14 +107591,6 @@ static PyMethodDef SwigMethods[] = {
 		"const DistributionHandler & Simulation::getDistributionHandler() const \n"
 		"\n"
 		""},
-	 { (char *)"Simulation_setProgressHandler", _wrap_Simulation_setProgressHandler, METH_VARARGS, (char *)"\n"
-		"Simulation_setProgressHandler(Simulation self, ProgressHandler * progress)\n"
-		"\n"
-		"void Simulation::setProgressHandler(ProgressHandler *progress)\n"
-		"\n"
-		"sets progress handler (used by GUI) \n"
-		"\n"
-		""},
 	 { (char *)"Simulation_setOptions", _wrap_Simulation_setOptions, METH_VARARGS, (char *)"\n"
 		"Simulation_setOptions(Simulation self, SimulationOptions options)\n"
 		"\n"
@@ -107637,6 +107604,7 @@ static PyMethodDef SwigMethods[] = {
 		"SimulationOptions& Simulation::getOptions()\n"
 		"\n"
 		""},
+	 { (char *)"Simulation_progressHandler", _wrap_Simulation_progressHandler, METH_VARARGS, (char *)"Simulation_progressHandler(Simulation self) -> ProgressHandler &"},
 	 { (char *)"Simulation_swigregister", Simulation_swigregister, METH_VARARGS, NULL},
 	 { (char *)"new_SimulationOptions", _wrap_new_SimulationOptions, METH_VARARGS, (char *)"\n"
 		"new_SimulationOptions() -> SimulationOptions\n"
@@ -110212,7 +110180,6 @@ static PyMethodDef SwigMethods[] = {
 		""},
 	 { (char *)"IsGISAXSDetector_swigregister", IsGISAXSDetector_swigregister, METH_VARARGS, NULL},
 	 { (char *)"new_Lattice", _wrap_new_Lattice, METH_VARARGS, (char *)"\n"
-		"Lattice()\n"
 		"Lattice(kvector_t a1, kvector_t a2, kvector_t a3)\n"
 		"new_Lattice(Lattice lattice) -> Lattice\n"
 		"\n"
diff --git a/cmake/bornagain/modules/SetupCoverage.cmake b/cmake/bornagain/modules/SetupCoverage.cmake
index 811b7bb0151..9842182f80d 100644
--- a/cmake/bornagain/modules/SetupCoverage.cmake
+++ b/cmake/bornagain/modules/SetupCoverage.cmake
@@ -8,6 +8,7 @@
 
 set(coverage_ignore_dirs "'/usr/*'")
 list(APPEND coverage_ignore_dirs "'*/auto/*'")
+list(APPEND coverage_ignore_dirs "'*/Tests/*'")
 list(APPEND coverage_ignore_dirs "'*/ThirdParty/*'")
 list(APPEND coverage_ignore_dirs "'*/build/*'")
 list(APPEND coverage_ignore_dirs "'*/GUI/externals/*'")
-- 
GitLab