From 1570eb840f238b61914d8ac05d61826b4a1b54c5 Mon Sep 17 00:00:00 2001
From: pospelov <pospelov@fz-juelich.de>
Date: Fri, 8 Jun 2012 18:05:16 +0200
Subject: [PATCH] new Convolve class for fast fourier transform convolution in
 2d

---
 App/App.pro                       |   6 +-
 App/inc/TestInstrument.h          |  45 ++
 App/src/DrawHelper.cpp            |   7 +-
 App/src/TestDiffuseReflection.cpp |  11 +-
 App/src/TestFactory.cpp           |   2 +
 App/src/TestFresnelCoeff.cpp      |   4 +-
 App/src/TestInstrument.cpp        | 364 +++++++++++++++
 Core/Core.pro                     |   6 +-
 Core/inc/Convolve.h               | 162 +++++++
 Core/inc/Exceptions.h             |   6 +
 Core/inc/MathFunctions.h          |   2 +
 Core/src/Convolve.cpp             | 750 ++++++++++++++++++++++++++++++
 Core/src/Exceptions.cpp           |   6 +
 Core/src/IFunctionalTest.cpp      |   2 +-
 Core/src/MathFunctions.cpp        |  20 +
 GISASFW.pro                       |   3 +-
 16 files changed, 1380 insertions(+), 16 deletions(-)
 create mode 100644 App/inc/TestInstrument.h
 create mode 100644 App/src/TestInstrument.cpp
 create mode 100644 Core/inc/Convolve.h
 create mode 100644 Core/src/Convolve.cpp

diff --git a/App/App.pro b/App/App.pro
index e3aac81a099..5e1188a8c84 100644
--- a/App/App.pro
+++ b/App/App.pro
@@ -16,7 +16,8 @@ SOURCES += \
     src/StandardSamples.cpp \
     src/CommandLine.cpp \
     src/TestFactory.cpp \
-    src/TestDiffuseReflection.cpp
+    src/TestDiffuseReflection.cpp \
+    src/TestInstrument.cpp
 
 HEADERS += \
     inc/DrawHelper.h \
@@ -30,7 +31,8 @@ HEADERS += \
     inc/StandardSamples.h \
     inc/CommandLine.h \
     inc/TestFactory.h \
-    inc/TestDiffuseReflection.h
+    inc/TestDiffuseReflection.h \
+    inc/TestInstrument.h
 
 INCLUDEPATH += ./inc
 DEPENDPATH += ./inc
diff --git a/App/inc/TestInstrument.h b/App/inc/TestInstrument.h
new file mode 100644
index 00000000000..79073497c70
--- /dev/null
+++ b/App/inc/TestInstrument.h
@@ -0,0 +1,45 @@
+#ifndef TESTINSTRUMENT_H
+#define TESTINSTRUMENT_H
+// ********************************************************************
+// * The BornAgain project                                            *
+// * Simulation of neutron and x-ray scattering at grazing incidence  *
+// *                                                                  *
+// * LICENSE AND DISCLAIMER                                           *
+// * Lorem ipsum dolor sit amet, consectetur adipiscing elit.  Mauris *
+// * eget quam orci. Quisque  porta  varius  dui,  quis  posuere nibh *
+// * mollis quis. Mauris commodo rhoncus porttitor.                   *
+// ********************************************************************
+//! @file   TestConvolution.h
+//! @brief  Definition of TestConvolution class for testing of Convolve class
+//! @author Scientific Computing Group at FRM II
+//! @date   02.05.2012
+
+#include "IFunctionalTest.h"
+#include "Convolve.h"
+#include <string>
+#include <vector>
+
+
+//- -------------------------------------------------------------------
+//! @class TestConvolution
+//! @brief Testing Convolve class for instrumental effects studies
+//- -------------------------------------------------------------------
+class TestInstrument : public IFunctionalTest
+{
+public:
+    TestInstrument();
+
+    void execute();
+
+    //! testing convolution in 1d
+    void test_convolve1d();
+
+    //! testing convolution in 2d
+    void test_convolve2d();
+
+private:
+    typedef std::pair<std::string, MathFunctions::Convolve::Mode> mode_pair_t;
+    std::vector<mode_pair_t> m_modes;
+};
+
+#endif // TESTINSTRUMENT_H
diff --git a/App/src/DrawHelper.cpp b/App/src/DrawHelper.cpp
index ffbfaba2b22..132e1b8fe0c 100644
--- a/App/src/DrawHelper.cpp
+++ b/App/src/DrawHelper.cpp
@@ -162,12 +162,13 @@ void DrawHelper::SetStyle()
     scattStyle->SetMarkerSize(0.2);
     scattStyle->SetHistLineWidth(2.);
     scattStyle->SetLineStyleString(2,"[12 12]"); // postscript dashes
+    scattStyle->SetFuncWidth(1.);
 
     // get rid of X error bars and y error bar caps
     //scattStyle->SetErrorX(0.001);
 
     // do not display any of the standard histogram decorations
-    scattStyle->SetOptTitle(0);
+    scattStyle->SetOptTitle(1);
     //scattStyle->SetOptStat(1111);
     scattStyle->SetOptStat(1);
     //scattStyle->SetOptFit(1111);
@@ -266,7 +267,7 @@ void DrawHelper::DrawMultilayer(const MultiLayer *sample)
     double z2 = sample->getLayerBottomZ(nlayers-1);
 
     double size = std::abs(z2 - z1);  // typical size of sample
-    double margin = size*0.02;        // safety margin to avoid overlapping of layers
+    double margin = size*0.01;        // safety margin to avoid overlapping of layers
     double fake_thickness = size*0.3; // thickness for layers without defined thickness (like ambience&substrate)
 
     // frame to draw
@@ -277,7 +278,7 @@ void DrawHelper::DrawMultilayer(const MultiLayer *sample)
 
     // drawing properties for interfaces, material and their names
     TLine interface;
-    interface.SetLineWidth(3);
+    interface.SetLineWidth(1);
     interface.SetLineStyle(3);
     interface.SetLineColor(kRed);
     TBox bulk;
diff --git a/App/src/TestDiffuseReflection.cpp b/App/src/TestDiffuseReflection.cpp
index c96900e5042..ddb74fcbb40 100644
--- a/App/src/TestDiffuseReflection.cpp
+++ b/App/src/TestDiffuseReflection.cpp
@@ -35,7 +35,7 @@ void TestDiffuseReflection::execute()
     DWBADiffuseReflection calc;
 
     std::vector<std::string > snames;
-    //snames.push_back("MultilayerOffspecTestcase1a");
+    snames.push_back("MultilayerOffspecTestcase1a");
     snames.push_back("MultilayerOffspecTestcase1b");
     //snames.push_back("MultilayerOffspecTestcase2a");
     //snames.push_back("MultilayerOffspecTestcase2b");
@@ -162,11 +162,12 @@ void TestDiffuseReflection::draw()
     TCanvas *c1 = new TCanvas(cname.c_str(),"Diffuse reflection",1024,768);
     DrawHelper::instance().SetMagnifier(c1);
 
-    c1->Divide(2,2);
-    c1->cd(1);
-    gr->Draw("apl");
+//    c1->Divide(2,2);
+//    c1->cd(1);
+//    gr->Draw("apl");
 
-    c1->cd(2);
+//    c1->cd(2);
+    c1->cd();
     gPad->SetRightMargin(0.2);
     gStyle->SetPalette(1);
     gPad->SetLogz();
diff --git a/App/src/TestFactory.cpp b/App/src/TestFactory.cpp
index c4cf7dad00a..97b6cf7705f 100644
--- a/App/src/TestFactory.cpp
+++ b/App/src/TestFactory.cpp
@@ -4,6 +4,7 @@
 #include "TestFormFactor.h"
 #include "TestDWBAFormFactor.h"
 #include "TestDiffuseReflection.h"
+#include "TestInstrument.h"
 
 #include "TBenchmark.h"
 
@@ -19,6 +20,7 @@ TestFactory::TestFactory() : m_benchmark(0)
     registerItem("formfactor", IFactoryCreateFunction<TestFormFactor, IFunctionalTest> );
     registerItem("dwba",       IFactoryCreateFunction<TestDWBAFormFactor, IFunctionalTest> );
     registerItem("diffuse",    IFactoryCreateFunction<TestDiffuseReflection, IFunctionalTest> );
+    registerItem("instrument", IFactoryCreateFunction<TestInstrument, IFunctionalTest> );
 
     m_benchmark = new TBenchmark();
 }
diff --git a/App/src/TestFresnelCoeff.cpp b/App/src/TestFresnelCoeff.cpp
index efe08ef1435..58778877c4d 100644
--- a/App/src/TestFresnelCoeff.cpp
+++ b/App/src/TestFresnelCoeff.cpp
@@ -35,10 +35,10 @@ void TestFresnelCoeff::execute()
     std::cout << "TestFresnelCoeff::execute() -> Info." << std::endl;
 
     std::vector<std::string > snames;
-//    snames.push_back("AirOnSubstrate");
+    snames.push_back("AirOnSubstrate");
 //    snames.push_back("SubstrateOnSubstrate");
 //    snames.push_back("SimpleMultilayer");
-    snames.push_back("MultilayerOffspecTestcase1a");
+//    snames.push_back("MultilayerOffspecTestcase1a");
 
     // loop over standard samples defined in SampleFactory and StandardSamples
     for(size_t i_sample=0; i_sample<snames.size(); i_sample++){
diff --git a/App/src/TestInstrument.cpp b/App/src/TestInstrument.cpp
new file mode 100644
index 00000000000..98923c866ad
--- /dev/null
+++ b/App/src/TestInstrument.cpp
@@ -0,0 +1,364 @@
+#include "TestInstrument.h"
+#include "MathFunctions.h"
+#include "Convolve.h"
+#include "DrawHelper.h"
+
+#include <iostream>
+
+#include "TMath.h"
+#include "TH1F.h"
+#include "TH2F.h"
+#include "TF1.h"
+#include "TF2.h"
+#include "TRandom.h"
+#include "TCanvas.h"
+#include "TProfile.h"
+#include "TGraph.h"
+#include "TBenchmark.h"
+#include "TStyle.h"
+
+std::vector<double > Convolve_MyFFT1D_B(int mode, const std::vector<double> &data1, const std::vector<double> &data2);
+
+
+int npeaks(30);
+
+Double_t fpeaks(Double_t *x, Double_t *par) {
+   Double_t result = par[0] + par[1]*x[0];
+   for (Int_t p=0;p<npeaks;p++) {
+      Double_t norm  = par[3*p+2];
+      Double_t mean  = par[3*p+3];
+      Double_t sigma = par[3*p+4];
+      result += norm*TMath::Gaus(x[0],mean,sigma);
+   }
+   return result;
+}
+
+
+
+
+TestInstrument::TestInstrument()
+{
+    std::cout << "TestInstrument::TestInstrument() -> Info." << std::endl;
+
+    m_modes.push_back( mode_pair_t("LINEAR_FULL", MathFunctions::Convolve::FFTW_LINEAR_FULL));
+    m_modes.push_back( mode_pair_t("LINEAR_SAME_UNPADDED", MathFunctions::Convolve::FFTW_LINEAR_SAME_UNPADDED));
+    m_modes.push_back( mode_pair_t("LINEAR_SAME", MathFunctions::Convolve::FFTW_LINEAR_SAME));
+    m_modes.push_back( mode_pair_t("CIRCULAR_SAME", MathFunctions::Convolve::FFTW_CIRCULAR_SAME));
+    m_modes.push_back( mode_pair_t("CIRCULAR_SAME_SHIFTED", MathFunctions::Convolve::FFTW_CIRCULAR_SAME_SHIFTED));
+}
+
+
+void TestInstrument::execute()
+{
+
+    test_convolve1d();
+    // results are about
+    // LINEAR_FULL: Real Time =   0.37 seconds Cpu Time =   0.36 seconds
+    // LINEAR_SAME_UNPADDED: Real Time =   1.03 seconds Cpu Time =   1.03 seconds
+    // LINEAR_SAME: Real Time =   0.23 seconds Cpu Time =   0.23 seconds
+    // CIRCULAR_SAME: Real Time =   0.50 seconds Cpu Time =   0.50 seconds
+    // CIRCULAR_SAME_SHIFTED: Real Time =   0.51 seconds Cpu Time =   0.51 seconds
+
+    //test_convolve2d();
+    // LINEAR_FULL: Real Time =   1.19 seconds Cpu Time =   1.18 seconds
+    // LINEAR_SAME_UNPADDED: Real Time =   0.72 seconds Cpu Time =   0.72 seconds
+    // LINEAR_SAME: Real Time =   0.72 seconds Cpu Time =   0.73 seconds
+    // CIRCULAR_SAME: Real Time =   0.53 seconds Cpu Time =   0.54 seconds
+    // CIRCULAR_SAME_SHIFTED: Real Time =   0.61 seconds Cpu Time =   0.61 seconds
+
+}
+
+
+
+void TestInstrument::test_convolve1d()
+{
+    npeaks = TMath::Abs(10);
+    double xmin(0.0);
+    double xmax(1000.);
+    size_t npoints = 501;
+    double dx = (xmax-xmin)/(npoints-1);
+
+    double sigma0=(xmax-xmin)*0.001;
+
+    TH1F *href = new TH1F("h","test", npoints-1, xmin, xmax);
+    href->SetMinimum(0.0);
+    href->SetMaximum(2.0);
+
+    //generate n peaks at random
+    Double_t par[3000];
+    par[0] = 1.0;
+    par[1] = -0.6/(xmax-xmin);
+    Int_t p;
+    for (p=0;p<npeaks;p++) {
+       par[3*p+2] = 1;
+       par[3*p+3] = (xmax-xmin)*0.01+gRandom->Rndm()*(xmax-xmin);
+       par[3*p+4] = sigma0+sigma0*5*gRandom->Rndm();
+    }
+    TF1 *fspect = new TF1("f",fpeaks,0,1000,2+3*npeaks);
+    fspect->SetNpx(10000);
+    fspect->SetParameters(par);
+    TCanvas *c1 = new TCanvas("c1_test_convolve1d","c1_test_convolve1d",1024, 768);
+    c1->Divide(3,3);
+    c1->cd(1);
+    //href->FillRandom("f",20000);
+    href->DrawCopy();
+    fspect->Draw("pl same");
+
+    c1->cd(2);
+    double sigma_measure = sigma0*5;
+    TH1F *hmeasured = new TH1F("hp","hp",500, xmin, xmax);
+//    double sigma_measure=sigma0*3;
+    for(int i=0; i<1000000; i++) {
+        hmeasured->Fill(fspect->GetRandom(xmin, xmax) + gRandom->Gaus(0.0,sigma_measure));
+    }
+    hmeasured->Scale(1./hmeasured->GetEntries());
+    hmeasured->SetLineColor(kRed);
+    hmeasured->Draw();
+
+    std::vector<double> xspect;
+    xspect.resize(npoints);
+    for(size_t i=0; i<npoints; i++) {
+        double x = xmin + i*dx;
+        xspect[i] = fspect->Eval(x);
+        std::cout << i << " " << x << " " << xspect[i] << std::endl;
+    }
+
+    // building responce function
+    std::vector<double> xresp;
+    xresp.resize(npoints);
+//    for(size_t i=0; i<npoints/2; i++) {
+//        double x = i*dx;
+//        double y = std::exp(-x*x/sigma_measure/sigma_measure);
+//        xresp[i] = y;
+//        xresp[npoints-1-i] = xresp[i];
+//    }
+
+    for(size_t i=0; i<npoints; i++) {
+        double x = -(xmax-xmin)/2.+i*dx;
+        double y = std::exp(-x*x/sigma_measure/sigma_measure);
+        xresp[i] = y;
+    }
+
+    TGraph *gr_resp = new TGraph(npoints);
+    for(size_t i=0; i<xresp.size(); i++){
+        double x = xmin + i*dx;
+        gr_resp->SetPoint(i,x,xresp[i]);
+    }
+    c1->cd(3);
+    href->DrawCopy();
+    gr_resp->Draw("pl same");
+
+
+    TBenchmark benchmark;
+    for(size_t i_mode=0; i_mode<m_modes.size(); i_mode++) {
+        std::string sname = m_modes[i_mode].first;
+        MathFunctions::Convolve::Mode mode = m_modes[i_mode].second;
+
+        // result
+        TGraph *gr_result = new TGraph(npoints);
+        benchmark.Start(sname.c_str());
+
+        MathFunctions::Convolve cv;
+        cv.setMode(mode);
+        std::vector<double> xresult;
+        for(int i=0; i<1000; i++) {
+            //int mm = (int)modes[i_mode];
+     //       xresult = Convolve_MyFFT1D_B(mm, xspect, xresp);
+            cv.fftconvolve(xspect, xresp, xresult);
+        }
+        benchmark.Stop(sname.c_str());
+        benchmark.Show(sname.c_str());
+        for(size_t i=0; i<xresult.size(); i++) {
+            double x = xmin + i*dx;
+            gr_result->SetPoint(i,x,xresult[i]);
+        }
+        c1->cd(4+i_mode);
+        href->SetMaximum(20.0);
+        href->DrawCopy();
+        gr_result->Draw("pl same");
+    }
+    float_t rp, cp;
+    std::cout << "--------------" << std::endl;
+    benchmark.Summary(rp, cp);
+
+}
+
+
+/* ************************************************************************* */
+//
+/* ************************************************************************* */
+void TestInstrument::test_convolve2d()
+{
+    double xmin(0.0), xmax(100.);
+    size_t npx = 100;
+    double dx = (xmax-xmin)/(npx);
+
+    double ymin(0.0), ymax(40.);
+    size_t npy = 40;
+    double dy = (ymax-ymin)/(npy);
+
+    double sigmax=(xmax-xmin)*0.01;
+    double sigmay=(ymax-ymin)*0.01;
+
+    // filling signal pattern - spikes on top of flat background
+    std::vector<std::vector<double> > source;
+    source.resize(npx);
+    for(size_t ix=0; ix<npx; ix++) {
+        source[ix].resize(npy, 1);
+        for(size_t iy=0; iy<npy; iy++){
+            // background
+            source[ix][iy] = 0.5*double(ix)/double(npx) + 0.5*double(iy)/double(npy);
+            // spikes
+            if(ix>1 && iy!=0) {
+                if( (ix%20==0 || (ix-1)%20==0) && iy%8==0) {
+                    std::cout << ix << " " << iy << std::endl;
+                    source[ix][iy]+=5;
+                }
+            }
+        } // iy
+    } // ix
+    // signal histogram
+    TH2F *h2_signal = new TH2F("h2_signal","h2_signal", npx, xmin, xmax, npy, ymin, ymax);
+    h2_signal->SetStats(0);
+    h2_signal->SetMinimum(0);
+    h2_signal->SetMaximum(3.0);
+    for(size_t ix=0; ix<npx; ix++){
+        double x = xmin +  dx*ix;
+        for(size_t iy=0; iy<npy; iy++){
+            double y = ymin + dy*iy;
+            h2_signal->Fill(x,y,source[ix][iy]);
+        }
+    }
+
+    // resolution function (two dimensional gaus)
+    TF2 *fres = new TF2("fres","[0]*TMath::Gaus(x,[1],[2])*TMath::Gaus(y,[3],[4])", xmin, xmax, ymin, ymax);
+    fres->SetParameters(1,xmin+(xmax-xmin)/2.,sigmax, ymin + (ymax-ymin)/2., sigmay);
+    // preparing resolution kernel and histogram
+    std::vector<std::vector<double> > kernel;
+    TH2F *h2_kernel = new TH2F("h2_kernel","h2_kernel", npx, xmin, xmax, npy, ymin, ymax);
+    kernel.resize(npx);
+    for(size_t ix=0; ix<npx; ix++) {
+        kernel[ix].resize(npy);
+        double x = xmin +  dx*ix;
+        for(size_t iy=0; iy<npy; iy++){
+            double y = ymin + dy*iy;
+            kernel[ix][iy] = fres->Eval(x,y);
+            h2_kernel->Fill(x,y, kernel[ix][iy]);
+        }
+    }
+
+    // simulation of measurements with smearing measurement with resolution function
+    TH2F *h2_measured = new TH2F("h2_measured","h2_measured", npx, xmin, xmax, npy, ymin, ymax);
+    TRandom mr;
+    for(int i_meas=0; i_meas<1000000; i_meas++) {
+        // generating distribution according to our signal
+        double x,y;
+        h2_signal->GetRandom2(x,y);
+        // smearing signal to get "measured" signal
+        h2_measured->Fill(x + mr.Gaus(0,sigmax), y + mr.Gaus(0,sigmay));
+    }
+
+    // drawing
+    TCanvas *c1 = new TCanvas("c1_test_convolve2d","c1_test_convolve2d",1024, 768);
+    DrawHelper::instance().SetMagnifier(c1);
+    gStyle->SetPalette(1);
+
+    c1->Divide(3,3);
+    c1->cd(1); gPad->SetRightMargin(0.15);
+    h2_signal->Draw("colz");
+
+    c1->cd(2); gPad->SetRightMargin(0.15);
+    h2_signal->Draw("lego");
+
+    c1->cd(3); gPad->SetRightMargin(0.15);
+    h2_kernel->Draw("lego");
+
+    c1->cd(4); gPad->SetRightMargin(0.15);
+    h2_measured->Draw("colz");
+
+    // calculation of convolution
+    std::vector<std::vector<double> > result;
+    TBenchmark benchmark;
+    for(size_t i_mode=0; i_mode<m_modes.size(); i_mode++) {
+        std::string sname = m_modes[i_mode].first;
+        MathFunctions::Convolve::Mode mode = m_modes[i_mode].second;
+
+        // running convolution several times to get statistics for benchmarking
+        benchmark.Start(sname.c_str());
+        MathFunctions::Convolve cv;
+        cv.setMode(mode);
+        for(int i=0; i<1000; i++) {
+            cv.fftconvolve(source, kernel, result);
+        }
+        benchmark.Stop(sname.c_str());
+
+        // drawing results of last convolution
+        c1->cd(5+i_mode);
+        TH2F h2_result("h2_result","h2_result", npx, xmin, xmax, npy, ymin, ymax);
+        h2_result.SetStats(0);
+        for(size_t ix=0; ix<npx; ix++) {
+            double x = xmin +  dx*ix;
+            for(size_t iy=0; iy<npy; iy++){
+                double y = ymin + dy*iy;
+                h2_result.Fill(x,y, result[ix][iy]);
+            }
+        }
+        h2_result.SetTitle(sname.c_str());
+        h2_result.DrawCopy("colz");
+    }
+
+    // benchmark summary
+    float_t rp, cp;
+    std::cout << "--------------" << std::endl;
+    benchmark.Summary(rp, cp);
+
+}
+
+
+
+
+std::vector<double> Convolve_MyFFT1D_B(int mode, const std::vector<double> &data1, const std::vector<double> &data2)
+{
+    size_t Ns = data1.size();
+    double * src = new double[Ns];
+    for(size_t i=0; i<Ns; i++) {
+        src[i] = data1[i];
+    }
+
+    size_t Nk = data2.size();
+    double * kernel = new double[Nk];
+    for(size_t i=0; i<Nk; i++) {
+        kernel[i] = data2[i];
+    }
+
+    FFTW_Workspace ws;
+//    init_workspace_fftw(ws, FFTW_LINEAR_FULL, 1,Ns,1,Nk);
+//    init_workspace_fftw(ws, FFTW_LINEAR_SAME_UNPADDED, 1,Ns,1,Nk);
+//    init_workspace_fftw(ws, FFTW_LINEAR_SAME, 1,Ns,1,Nk);
+//    init_workspace_fftw(ws, FFTW_LINEAR_VALID, 1,Ns,1,Nk);
+//    init_workspace_fftw(ws, FFTW_CIRCULAR_SAME, 1,Ns,1,Nk);
+//    init_workspace_fftw(ws, FFTW_CIRCULAR_SAME_SHIFTED, 1,Ns,1,Nk);
+
+    FFTW_Convolution_Mode xm = (FFTW_Convolution_Mode)mode;
+    init_workspace_fftw(ws, xm, 1,Ns,1,Nk);
+
+
+    fftw_convolve(ws, src, kernel);
+
+//    std::cout << "!!! " << ws.w_dst << std::endl;
+//    for(int j = 0 ; j < ws.w_dst ; ++j) std::cout << ws.dst[j] << " ";
+
+    std::vector<double > fft_result;
+    fft_result.resize(ws.w_dst);
+    for(size_t i=0; i<data1.size(); i++){
+        fft_result[i] = ws.dst[i];
+    }
+
+    clear_workspace_fftw(ws);
+    return fft_result;
+}
+
+
+
+
+
diff --git a/Core/Core.pro b/Core/Core.pro
index a447ecc215f..d82f519cbda 100644
--- a/Core/Core.pro
+++ b/Core/Core.pro
@@ -36,7 +36,8 @@ SOURCES += \
     src/ISingleton.cpp \
     src/IFunctionalTest.cpp \
     src/IFactory.cpp \
-    src/DWBADiffuseReflection.cpp
+    src/DWBADiffuseReflection.cpp \
+    src/Convolve.cpp
 
 HEADERS += \
     inc/ISample.h \
@@ -72,7 +73,8 @@ HEADERS += \
     inc/IFunctionalTest.h \
     inc/IFactory.h \
     inc/Numeric.h \
-    inc/DWBADiffuseReflection.h
+    inc/DWBADiffuseReflection.h \
+    inc/Convolve.h
 
 INCLUDEPATH += ./inc
 DEPENDPATH += ./inc
diff --git a/Core/inc/Convolve.h b/Core/inc/Convolve.h
new file mode 100644
index 00000000000..2ba520f64b6
--- /dev/null
+++ b/Core/inc/Convolve.h
@@ -0,0 +1,162 @@
+#ifndef CONVOLVE_H
+#define CONVOLVE_H
+// ********************************************************************
+// * The BornAgain project                                            *
+// * Simulation of neutron and x-ray scattering at grazing incidence  *
+// *                                                                  *
+// * LICENSE AND DISCLAIMER                                           *
+// * Lorem ipsum dolor sit amet, consectetur adipiscing elit.  Mauris *
+// * eget quam orci. Quisque  porta  varius  dui,  quis  posuere nibh *
+// * mollis quis. Mauris commodo rhoncus porttitor.                   *
+// ********************************************************************
+//! @file   Convolve.h
+//! @brief  Definition of Convolve class
+//! @author Scientific Computing Group at FRM II
+//! @date   30.05.2012
+
+
+#include <fftw3.h>
+#include <vector>
+
+
+
+namespace MathFunctions
+{
+
+
+//- -------------------------------------------------------------------
+//! @class Convolve
+//! @brief Convolution of two real vectors (1D or 2D ) using Fast Fourier Transformation.
+//!
+//! Usage:
+//! std::vector<double> signal, kernel, result;
+//! Convolve cv;
+//! cv.fftconvolve(signal, kernel, result)
+//!
+//! Given code rely on code from Jeremy Fix page
+//! http://jeremy.fix.free.fr/spip.php?article15
+//! see also
+//! "Efficient convolution using the Fast Fourier Transform, Application in C++"
+//! by Jeremy Fix, May 30, 2011
+//- -------------------------------------------------------------------
+class Convolve
+{
+public:
+    Convolve();
+    ~Convolve();
+
+    //! definition of 1d vector of double
+    typedef std::vector<double> double1d_t;
+
+    //! definition of 2d vector of double
+    typedef std::vector<double1d_t> double2d_t;
+
+    //! convolution  modes
+    enum Mode { FFTW_LINEAR_FULL, FFTW_LINEAR_SAME_UNPADDED, FFTW_LINEAR_SAME, FFTW_LINEAR_VALID, FFTW_CIRCULAR_SAME, FFTW_CIRCULAR_SAME_SHIFTED, FFTW_UNDEFINED };
+
+    //! convolution in 1D
+    void fftconvolve(const double1d_t &source, const double1d_t &kernel, double1d_t &result);
+
+    //! convolution in 2D
+    void fftconvolve(const double2d_t &source, const double2d_t &kernel, double2d_t &result);
+
+    //! prepare arrays for 2D convolution of given vectors
+    void init(int h_src, int w_src, int h_kernel, int w_kernel);
+
+    //! set convolution mode
+    void setMode(Mode mode) { m_mode = mode; }
+
+private:
+    //! compute convolution of source and kernel using fast fourier transformation
+    void fftw_convolve(const double2d_t &source, const double2d_t &kernel);
+    //void fftw_convolve(double * src,double * kernel);
+
+    //! compute circual convolution of source and kernel using fast fourier transformation
+    //void fftw_circular_convolution(double * src, double * kernel);
+    void fftw_circular_convolution(const double2d_t &source, const double2d_t &kernel);
+
+    //! find closest number X>n,  which  can be factorised according to fftw3 favorite factorisation
+    int find_closest_factor(int n);
+
+    //! if a number can be factorised using only favorite fftw3 factors
+    bool is_optimal(int n);
+
+    //! Workspace contains input (source and kernel), intermediate and output arrays to run convolution via fft
+    //! 'source' it is our signal, 'kernel' it is our resolution (or delta-responce) function
+    //! Sizes of input arrays are adjusted; output arrays are alocated via fftw3 allocation for maximum performance
+    class Workspace
+    {
+    public:
+        Workspace();
+        ~Workspace();
+        void clear();
+        friend class Convolve;
+    private:
+        int h_src, w_src;                 // size of original 'source' array in 2 dimensions
+        int h_kernel, w_kernel;           // size of original 'kernel' array in 2 dimensions
+        int w_fftw, h_fftw;               // size of adjusted source and kernel arrays (in_src, out_src, in_kernel, out_kernel)
+        double *in_src;                   // adjusted input 'source' array
+        double *out_src;                  // result of fourier transformation of source
+        double *in_kernel;                // adjusted input 'kernel' array
+        double *out_kernel;               // result of fourier transformation of kernel
+        double *dst_fft;                  // result of production of FFT(source) and FFT(kernel)
+        int h_dst, w_dst;                 // size of resulting array
+        double *dst;                      // The array containing the result
+        fftw_plan p_forw_src;
+        fftw_plan p_forw_kernel;
+        fftw_plan p_back;
+    };
+
+    Workspace ws; // input and output data for fftw3
+    Mode m_mode;  // convolution mode
+    std::vector<size_t > m_implemented_factors; // favorite factorization terms of fftw3
+};
+
+
+} // namespace MathFunctions
+
+
+typedef enum
+{
+    FFTW_LINEAR_FULL,
+    FFTW_LINEAR_SAME_UNPADDED,
+    FFTW_LINEAR_SAME,
+    FFTW_LINEAR_VALID,
+    FFTW_CIRCULAR_SAME,
+    FFTW_CIRCULAR_SAME_SHIFTED,
+    FFTW_CIRCULAR_CUSTOM
+} FFTW_Convolution_Mode;
+
+typedef struct FFTW_Workspace
+{
+    //fftw_complex * in_src, *out_src;
+    double * in_src, *out_src, *in_kernel, *out_kernel;
+    int h_src, w_src, h_kernel, w_kernel;
+    int w_fftw, h_fftw;
+    FFTW_Convolution_Mode mode;
+    double * dst_fft;
+    double * dst; // The array containing the result
+    int h_dst, w_dst; // its size ; This is automatically set by init_workspace, despite you use FFTW_CIRCULAR_CUSTOM
+    fftw_plan p_forw_src;
+    fftw_plan p_forw_kernel;
+    fftw_plan p_back;
+
+} FFTW_Workspace;
+
+extern void factorize (const int n,
+                int *n_factors,
+                int factors[],
+                int * implemented_factors);
+
+extern bool is_optimal(int n, int * implemented_factors);
+extern int find_closest_factor(int n, int * implemented_factor);
+extern void init_workspace_fftw(FFTW_Workspace & ws, FFTW_Convolution_Mode mode, int h_src, int w_src, int h_kernel, int w_kernel);
+extern void clear_workspace_fftw(FFTW_Workspace & ws);
+extern void fftw_convolve(FFTW_Workspace &ws, double * src,double * kernel);
+extern void fftw_circular_convolution(FFTW_Workspace &ws, double * src, double * kernel);
+
+
+
+
+
+#endif // CONVOLVE_H
diff --git a/Core/inc/Exceptions.h b/Core/inc/Exceptions.h
index d35aba238c5..fd740afb836 100644
--- a/Core/inc/Exceptions.h
+++ b/Core/inc/Exceptions.h
@@ -72,6 +72,12 @@ public:
     LogicErrorException(const std::string& message);
 };
 
+class RuntimeErrorException : public std::runtime_error
+{
+public:
+    RuntimeErrorException(const std::string& message);
+};
+
 class DivisionByZeroException : public std::runtime_error
 {
 public:
diff --git a/Core/inc/MathFunctions.h b/Core/inc/MathFunctions.h
index 6a205a81bd5..93f0003eff9 100644
--- a/Core/inc/MathFunctions.h
+++ b/Core/inc/MathFunctions.h
@@ -42,6 +42,8 @@ std::vector<complex_t > FastFourierTransform(const std::vector<complex_t > &data
 
 std::vector<complex_t > FastFourierTransform(const std::vector<double > &data, TransformCase tcase);
 
+std::vector<complex_t> ConvolveFFT(const std::vector<double> &signal, const std::vector<double> &resfunc);
+
 } // Namespace MathFunctions
 
 inline double MathFunctions::GenerateNormalRandom(double average, double std_dev)
diff --git a/Core/src/Convolve.cpp b/Core/src/Convolve.cpp
new file mode 100644
index 00000000000..fa2e7c70bc7
--- /dev/null
+++ b/Core/src/Convolve.cpp
@@ -0,0 +1,750 @@
+#include "Convolve.h"
+#include <iostream>
+#include <stdexcept>
+#include "Exceptions.h"
+
+MathFunctions::Convolve::Convolve() : m_mode(FFTW_UNDEFINED)
+{
+    // storing favorite fftw3 prime factors
+    const size_t FFTW_FACTORS[] = {13,11,7,5,3,2};
+    m_implemented_factors.assign(FFTW_FACTORS, FFTW_FACTORS + sizeof(FFTW_FACTORS)/sizeof(FFTW_FACTORS[0]));
+}
+
+
+
+MathFunctions::Convolve::~Convolve()
+{
+
+}
+
+
+MathFunctions::Convolve::Workspace::Workspace() :
+    h_src(0), w_src(0), h_kernel(0), w_kernel(0),
+    w_fftw(0), h_fftw(0), in_src(0), out_src(0), in_kernel(0), out_kernel(0), dst_fft(0), h_dst(0), w_dst(0), dst(0),
+    p_forw_src(NULL), p_forw_kernel(NULL), p_back(NULL)
+{
+
+}
+
+
+MathFunctions::Convolve::Workspace::~Workspace()
+{
+    clear();
+}
+
+
+void MathFunctions::Convolve::Workspace::clear()
+{
+    h_src=0;
+    w_src=0;
+    h_kernel=0;
+    w_kernel = 0;
+    if(in_src) delete[] in_src;
+    in_src = 0;
+
+    if(out_src) fftw_free((fftw_complex*)out_src);
+    out_src = 0;
+
+    if(in_kernel) delete[] in_kernel;
+    in_kernel=0;
+
+    if(out_kernel) fftw_free((fftw_complex*)out_kernel);
+    out_kernel=0;
+
+    if(dst_fft) delete[] dst_fft;
+    dst_fft=0;
+
+    if(dst) delete[] dst;
+    dst=0;
+
+    if(p_forw_src != NULL) fftw_destroy_plan(p_forw_src);
+    if(p_forw_kernel != NULL) fftw_destroy_plan(p_forw_kernel);
+    if(p_back != NULL)  fftw_destroy_plan(p_back);
+
+    // this returns fftw3 into completely initial state but is dramatically slow
+    //fftw_cleanup();
+}
+
+
+/* ************************************************************************* */
+// convolution in dd
+/* ************************************************************************* */
+void MathFunctions::Convolve::fftconvolve(const double2d_t &source, const double2d_t &kernel, double2d_t &result)
+{
+    // set default convolution mode, if not defined
+    if(m_mode == FFTW_UNDEFINED) {
+        std::cout << "setting FFTW_LINEAR_SAME" << std::endl;
+        setMode(FFTW_LINEAR_SAME);
+    }
+
+    size_t h_src = source.size();
+    size_t w_src = (source.size() ? source[0].size() : 0);
+    size_t h_kernel = kernel.size();
+    size_t w_kernel = (kernel.size() ? kernel[0].size() : 0);
+
+    if(!h_src || !w_src || !h_kernel || !w_kernel) {
+        std::cout << "MathFunctions::Convolve::fftconvolve() -> Panic! Wrong dimensions " << h_src << " " << w_src << " " << h_kernel << " " << w_kernel << std::endl;
+        throw std::runtime_error("XXX");
+    }
+
+    init(h_src, w_src, h_kernel, w_kernel);
+
+//    double *src = new double[h_src*w_src];
+//    for(size_t i=0; i<h_src; i++) {
+//        for(size_t j=0; j<w_src; j++) {
+//            src[i*w_src + j] = source[i][j];
+//        }
+//    }
+
+//    double *krn = new double[h_kernel*w_kernel];
+//    for(size_t i=0; i<h_kernel; i++) {
+//        for(size_t j=0; j<w_kernel; j++) {
+//            krn[i*w_kernel + j] = kernel[i][j];
+//        }
+//    }
+
+//    fftw_convolve(src, krn);
+    fftw_convolve(source, kernel);
+
+//    delete [] src;
+//    delete [] krn;
+
+    // results
+    result.clear();
+    result.resize(ws.h_dst);
+    for(int i=0; i<ws.h_dst; i++) {
+        result[i].resize(ws.w_dst,0);
+        for(int j=0; j<ws.w_dst; j++) {
+            result[i][j]=ws.dst[i*ws.w_dst+j];
+        }
+    }
+
+}
+
+
+/* ************************************************************************* */
+// convolution in 1d
+/* ************************************************************************* */
+void MathFunctions::Convolve::fftconvolve(const double1d_t &source, const double1d_t &kernel, double1d_t &result)
+{
+    // we simply create 2d arrays with length of first dimension equal to 1, and call 2d convolution
+    double2d_t source2d, kernel2d;
+    source2d.push_back(source);
+    kernel2d.push_back(kernel);
+
+    double2d_t result2d;
+    fftconvolve(source2d, kernel2d, result2d);
+    if(result2d.size() != 1) {
+        std::cout << "MathFunctions::Convolve::fftconvolve -> Panic in 1d" << std::endl;
+        throw std::runtime_error("Panic!");
+    }
+    result = result2d[0];
+}
+
+
+/* ************************************************************************* */
+// initialise input and output arrays for fast fourier transformation
+/* ************************************************************************* */
+void MathFunctions::Convolve::init(int h_src, int w_src, int h_kernel, int w_kernel)
+{
+    ws.clear();
+    ws.h_src = h_src;
+    ws.w_src = w_src;
+    ws.h_kernel = h_kernel;
+    ws.w_kernel = w_kernel;
+    switch(m_mode)
+    {
+    case FFTW_LINEAR_FULL:
+        // Full Linear convolution
+        ws.h_fftw = find_closest_factor(h_src + h_kernel - 1);
+        ws.w_fftw = find_closest_factor(w_src + w_kernel - 1);
+        ws.h_dst = h_src + h_kernel-1;
+        ws.w_dst = w_src + w_kernel-1;
+        break;
+    case FFTW_LINEAR_SAME_UNPADDED:
+        // Same Linear convolution
+        ws.h_fftw = h_src + int(h_kernel/2.0);
+        ws.w_fftw = w_src + int(w_kernel/2.0);
+        ws.h_dst = h_src;
+        ws.w_dst = w_src;
+        break;
+    case FFTW_LINEAR_SAME:
+        // Same Linear convolution
+        ws.h_fftw = find_closest_factor(h_src + int(h_kernel/2.0));
+        ws.w_fftw = find_closest_factor(w_src + int(w_kernel/2.0));
+        ws.h_dst = h_src;
+        ws.w_dst = w_src;
+        break;
+    case FFTW_LINEAR_VALID:
+        // Valid Linear convolution
+        if(ws.h_kernel > ws.h_src || ws.w_kernel > ws.w_src)
+        {
+            ws.h_fftw = 0;
+            ws.w_fftw = 0;
+            ws.h_dst = 0;
+            ws.w_dst = 0;
+            std::cout << "The 'valid' convolution results in an empty matrix" << std::endl;
+            throw std::runtime_error("The 'valid' convolution results in an empty matrix");
+        } else {
+            ws.h_fftw = find_closest_factor(h_src);
+            ws.w_fftw = find_closest_factor(w_src);
+            ws.h_dst = h_src - h_kernel+1;
+            ws.w_dst = w_src - w_kernel+1;
+        }
+        break;
+    case FFTW_CIRCULAR_SAME:
+        // Circular convolution, modulo N
+        // We don't padd with zeros because if we do, we need to padd with at least h_kernel/2; w_kernel/2 elements
+        // plus the wrapp around
+        // which in facts leads to too much computations compared to the gain obtained with the optimal size
+        ws.h_fftw = h_src;
+        ws.w_fftw = w_src;
+        ws.h_dst = h_src;
+        ws.w_dst = w_src;
+        break;
+    case FFTW_CIRCULAR_SAME_SHIFTED:
+        // Circular convolution, modulo N, shifted by M/2
+        // We don't padd with zeros because if we do, we need to padd with at least h_kernel/2; w_kernel/2 elements
+        // plus the wrapp around
+        // which in facts leads to too much computations compared to the gain obtained with the optimal size
+        ws.h_fftw = h_src;
+        ws.w_fftw = w_src;
+        ws.h_dst = h_src;
+        ws.w_dst = w_src;
+        break;
+    default:
+        std::cout << "Unrecognized convolution mode, possible modes are FFTW_LINEAR_FULL, FFTW_LINEAR_SAME, FFTW_LINEAR_SAME_UNPADDED, FFTW_LINEAR_VALID, FFTW_CIRCULAR_SAME, FFTW_CIRCULAR_SHIFTED " << std::endl;
+    }
+
+    ws.in_src = new double[ws.h_fftw * ws.w_fftw];
+    ws.out_src = (double*) fftw_malloc(sizeof(fftw_complex) * ws.h_fftw * (ws.w_fftw/2+1));
+    ws.in_kernel = new double[ws.h_fftw * ws.w_fftw];
+    ws.out_kernel = (double*) fftw_malloc(sizeof(fftw_complex) * ws.h_fftw * (ws.w_fftw/2+1));
+
+    ws.dst_fft = new double[ws.h_fftw * ws.w_fftw];
+    ws.dst = new double[ws.h_dst * ws.w_dst];
+
+    // Initialization of the plans
+    ws.p_forw_src = fftw_plan_dft_r2c_2d(ws.h_fftw, ws.w_fftw, ws.in_src, (fftw_complex*)ws.out_src, FFTW_ESTIMATE);
+    if( ws.p_forw_src == NULL ) throw RuntimeErrorException("MathFunctions::Convolve::init() -> Error! Can't initialise p_forw_src plan.");
+
+    ws.p_forw_kernel = fftw_plan_dft_r2c_2d(ws.h_fftw, ws.w_fftw, ws.in_kernel, (fftw_complex*)ws.out_kernel, FFTW_ESTIMATE);
+    if( ws.p_forw_kernel == NULL ) throw RuntimeErrorException("MathFunctions::Convolve::init() -> Error! Can't initialise p_forw_kernel plan.");
+
+    // The backward FFT takes ws.out_kernel as input
+    ws.p_back = fftw_plan_dft_c2r_2d(ws.h_fftw, ws.w_fftw, (fftw_complex*)ws.out_kernel, ws.dst_fft, FFTW_ESTIMATE);
+    if( ws.p_back == NULL ) throw RuntimeErrorException("MathFunctions::Convolve::init() -> Error! Can't initialise p_back plan.");
+
+
+}
+
+
+/* ************************************************************************* */
+// initialise input and output arrays for fast fourier transformation
+/* ************************************************************************* */
+//void MathFunctions::Convolve::fftw_circular_convolution(double * src, double * kernel)
+void MathFunctions::Convolve::fftw_circular_convolution(const double2d_t &src, const double2d_t &kernel)
+{
+    double * ptr, *ptr_end, *ptr2;
+
+    // Reset the content of ws.in_src
+    for(ptr = ws.in_src, ptr_end = ws.in_src + ws.h_fftw*ws.w_fftw ; ptr != ptr_end ; ++ptr)
+        *ptr = 0.0;
+    for(ptr = ws.in_kernel, ptr_end = ws.in_kernel + ws.h_fftw*ws.w_fftw ; ptr != ptr_end ; ++ptr)
+        *ptr = 0.0;
+
+    // Then we build our periodic signals
+    //ptr = src;
+    for(int i = 0 ; i < ws.h_src ; ++i)
+        for(int j = 0 ; j < ws.w_src ; ++j, ++ptr)
+            ws.in_src[(i%ws.h_fftw)*ws.w_fftw+(j%ws.w_fftw)] += src[i][j];
+            //ws.in_src[(i%ws.h_fftw)*ws.w_fftw+(j%ws.w_fftw)] += src[i*ws.w_src + j];
+
+    //ptr = kernel;
+    for(int i = 0 ; i < ws.h_kernel ; ++i)
+        for(int j = 0 ; j < ws.w_kernel ; ++j, ++ptr)
+            ws.in_kernel[(i%ws.h_fftw)*ws.w_fftw+(j%ws.w_fftw)] += kernel[i][j];
+            //ws.in_kernel[(i%ws.h_fftw)*ws.w_fftw+(j%ws.w_fftw)] += kernel[i*ws.w_kernel + j];
+
+    // And we compute their packed FFT
+    fftw_execute(ws.p_forw_src);
+    fftw_execute(ws.p_forw_kernel);
+
+    // Compute the element-wise product on the packed terms
+    // Let's put the element wise products in ws.in_kernel
+    double re_s, im_s, re_k, im_k;
+    for(ptr = ws.out_src, ptr2 = ws.out_kernel, ptr_end = ws.out_src+2*ws.h_fftw * (ws.w_fftw/2+1); ptr != ptr_end ; ++ptr, ++ptr2)
+    {
+        re_s = *ptr;
+        im_s = *(++ptr);
+        re_k = *ptr2;
+        im_k = *(++ptr2);
+        *(ptr2-1) = re_s * re_k - im_s * im_k;
+        *ptr2 = re_s * im_k + im_s * re_k;
+    }
+
+    // Compute the backward FFT
+    // Carefull, The backward FFT does not preserve the output
+    fftw_execute(ws.p_back);
+    // Scale the transform
+    for(ptr = ws.dst_fft, ptr_end = ws.dst_fft + ws.w_fftw*ws.h_fftw ; ptr != ptr_end ; ++ptr)
+        *ptr /= double(ws.h_fftw*ws.w_fftw);
+
+}
+
+
+
+/* ************************************************************************* */
+// initialise input and output arrays for fast fourier transformation
+/* ************************************************************************* */
+//void MathFunctions::Convolve::fftw_convolve(double * src,double * kernel)
+void MathFunctions::Convolve::fftw_convolve(const double2d_t &src, const double2d_t &kernel)
+{
+    if(ws.h_fftw <= 0 || ws.w_fftw <= 0)
+    {
+        std::cout << "MathFunctions::Convolve::fftw_convolve() -> Panic! Initialisation is missed." << std::endl;
+        throw std::runtime_error("MathFunctions::Convolve::fftw_convolve() -> Panic! Initialisation is missed.");
+    }
+
+    // Compute the circular convolution
+    fftw_circular_convolution(src, kernel);
+
+    // Depending on the type of convolution one is looking for, we extract the appropriate part of the result from out_src
+    int h_offset(0), w_offset(0);
+
+    switch(m_mode)
+    {
+    case FFTW_LINEAR_FULL:
+        // Full Linear convolution
+        // Here we just keep the first [0:h_dst-1 ; 0:w_dst-1] real part elements of out_src
+        for(int i = 0 ; i < ws.h_dst  ; ++i)
+            memcpy(&ws.dst[i*ws.w_dst], &ws.dst_fft[i*ws.w_fftw], ws.w_dst*sizeof(double));
+        break;
+    case FFTW_LINEAR_SAME_UNPADDED:
+        // Same linear convolution
+        // Here we just keep the first [h_filt/2:h_filt/2+h_dst-1 ; w_filt/2:w_filt/2+w_dst-1] real part elements of out_src
+        h_offset = int(ws.h_kernel/2.0);
+        w_offset = int(ws.w_kernel/2.0);
+        for(int i = 0 ; i < ws.h_dst ; ++i)
+            memcpy(&ws.dst[i*ws.w_dst], &ws.dst_fft[(i+h_offset)*ws.w_fftw+w_offset], ws.w_dst*sizeof(double));
+        break;
+    case FFTW_LINEAR_SAME:
+        // Same linear convolution
+        // Here we just keep the first [h_filt/2:h_filt/2+h_dst-1 ; w_filt/2:w_filt/2+w_dst-1] real part elements of out_src
+        h_offset = int(ws.h_kernel/2.0);
+        w_offset = int(ws.w_kernel/2.0);
+        for(int i = 0 ; i < ws.h_dst ; ++i)
+            memcpy(&ws.dst[i*ws.w_dst], &ws.dst_fft[(i+h_offset)*ws.w_fftw+w_offset], ws.w_dst*sizeof(double));
+        break;
+    case FFTW_LINEAR_VALID:
+        // Valid linear convolution
+        // Here we just take [h_dst x w_dst] elements starting at [h_kernel-1;w_kernel-1]
+        h_offset = ws.h_kernel - 1;
+        w_offset = ws.w_kernel - 1;
+        for(int i = 0 ; i < ws.h_dst ; ++i)
+            memcpy(&ws.dst[i*ws.w_dst], &ws.dst_fft[(i+h_offset)*ws.w_fftw+w_offset], ws.w_dst*sizeof(double));
+        break;
+    case FFTW_CIRCULAR_SAME:
+        // Circular convolution
+        // We copy the first [0:h_dst-1 ; 0:w_dst-1] real part elements of out_src
+        for(int i = 0 ; i < ws.h_dst ; ++i)
+            memcpy(&ws.dst[i*ws.w_dst], &ws.dst_fft[i*ws.w_fftw], ws.w_dst*sizeof(double) );
+        break;
+    case FFTW_CIRCULAR_SAME_SHIFTED:
+        // Shifted Circular convolution
+        // We copy the [h_kernel/2:h_kernel/2+h_dst-1 ; w_kernel/2:w_kernel/2+w_dst-1] real part elements of out_src
+        for(int i = 0 ; i < ws.h_dst ; ++i)
+            for(int j = 0 ; j < ws.w_dst ; ++j)
+                ws.dst[i*ws.w_dst + j] = ws.dst_fft[((i+int(ws.h_kernel/2.0))%ws.h_fftw)*ws.w_fftw+(j+int(ws.w_kernel/2.0))%ws.w_fftw];
+        break;
+    default:
+        std::cout << "Unrecognized convolution mode, possible modes are FFTW_LINEAR_FULL, FFTW_LINEAR_SAME, FFTW_LINEAR_SAME_UNPADDED, FFTW_LINEAR_VALID, FFTW_CIRCULAR_SAME, FFTW_CIRCULAR_SHIFTED " << std::endl;
+    }
+}
+
+
+
+/* ************************************************************************* */
+// find a number closest to the given one, which  can be factorised according
+// to fftw3 favorite factorisation
+/* ************************************************************************* */
+int MathFunctions::Convolve::find_closest_factor(int n)
+{
+    if(is_optimal(n) ) {
+        return n;
+    } else {
+        int j = n+1;
+        while( !is_optimal(j) ) ++j;
+        return j;
+    }
+}
+
+
+/* ************************************************************************* */
+// if a number can be factorised using only favorite fftw3 factors
+/* ************************************************************************* */
+bool MathFunctions::Convolve::is_optimal(int n)
+{
+    if(n==1) return false;
+    int ntest = n;
+    for(size_t i=0; i<m_implemented_factors.size(); i++){
+        while( (ntest%m_implemented_factors[i]) == 0 ) {
+            ntest = ntest/m_implemented_factors[i];
+        }
+    }
+    if(ntest==1) return true;
+    return false;
+}
+
+
+
+
+
+/////////////////
+///
+///
+
+
+// ******************** Begin of factorization code ***********************//
+// A piece of code to determine if a number "n" can be written as products of only the integers given in implemented_factors
+void factorize (const int n,
+                int *n_factors,
+                int factors[],
+                int * implemented_factors)
+{
+    int nf = 0;
+    int ntest = n;
+    int factor;
+    int i = 0;
+
+    if (n == 0)
+    {
+        printf("Length n must be positive integer\n");
+        return ;
+    }
+
+    if (n == 1)
+    {
+        factors[0] = 1;
+        *n_factors = 1;
+        return ;
+    }
+
+    /* deal with the implemented factors */
+
+    while (implemented_factors[i] && ntest != 1)
+    {
+        factor = implemented_factors[i];
+        while ((ntest % factor) == 0)
+        {
+            ntest = ntest / factor;
+            factors[nf] = factor;
+            nf++;
+        }
+        i++;
+    }
+
+    // Ok that's it
+    if(ntest != 1)
+    {
+        factors[nf] = ntest;
+        nf++;
+    }
+
+    /* check that the factorization is correct */
+    {
+        int product = 1;
+
+        for (i = 0; i < nf; i++)
+        {
+            product *= factors[i];
+        }
+
+        if (product != n)
+        {
+            printf("factorization failed");
+        }
+    }
+
+    *n_factors = nf;
+}
+
+bool is_optimal(int n, int * implemented_factors)
+{
+    int nf;
+    int factors[64];
+    int i = 0;
+    factorize(n, &nf, factors,implemented_factors);
+
+    // We just have to check if the last factor belongs to GSL_FACTORS
+    while(implemented_factors[i])
+    {
+        if(factors[nf-1] == implemented_factors[i])
+            return true;
+        ++i;
+    }
+    return false;
+}
+
+int find_closest_factor(int n, int * implemented_factor)
+{
+    int j;
+    if(is_optimal(n,implemented_factor))
+        return n;
+    else
+    {
+        j = n+1;
+        while(!is_optimal(j,implemented_factor))
+            ++j;
+        return j;
+    }
+}
+// ******************** End of factorization code ***********************//
+
+int FFTW_FACTORS[7] = {13,11,7,5,3,2,0}; // end with zero to detect the end of the array
+
+
+void init_workspace_fftw(FFTW_Workspace & ws, FFTW_Convolution_Mode mode, int h_src, int w_src, int h_kernel, int w_kernel)
+{
+    ws.h_src = h_src;
+    ws.w_src = w_src;
+    ws.h_kernel = h_kernel;
+    ws.w_kernel = w_kernel;
+    ws.mode = mode;
+
+    switch(mode)
+    {
+    case FFTW_LINEAR_FULL:
+        // Full Linear convolution
+        ws.h_fftw = find_closest_factor(h_src + h_kernel - 1,FFTW_FACTORS);
+        ws.w_fftw = find_closest_factor(w_src + w_kernel - 1,FFTW_FACTORS);
+        ws.h_dst = h_src + h_kernel-1;
+        ws.w_dst = w_src + w_kernel-1;
+        break;
+    case FFTW_LINEAR_SAME_UNPADDED:
+        // Same Linear convolution
+        ws.h_fftw = h_src + int(h_kernel/2.0);
+        ws.w_fftw = w_src + int(w_kernel/2.0);
+        ws.h_dst = h_src;
+        ws.w_dst = w_src;
+        break;
+    case FFTW_LINEAR_SAME:
+        // Same Linear convolution
+        ws.h_fftw = find_closest_factor(h_src + int(h_kernel/2.0),FFTW_FACTORS);
+        ws.w_fftw = find_closest_factor(w_src + int(w_kernel/2.0),FFTW_FACTORS);
+        ws.h_dst = h_src;
+        ws.w_dst = w_src;
+        break;
+    case FFTW_LINEAR_VALID:
+        // Valid Linear convolution
+        if(ws.h_kernel > ws.h_src || ws.w_kernel > ws.w_src)
+        {
+            printf("Warning : The 'valid' convolution results in an empty matrix\n");
+            ws.h_fftw = 0;
+            ws.w_fftw = 0;
+            ws.h_dst = 0;
+            ws.w_dst = 0;
+        }
+        else
+        {
+            ws.h_fftw = find_closest_factor(h_src, FFTW_FACTORS);
+            ws.w_fftw = find_closest_factor(w_src, FFTW_FACTORS);
+            ws.h_dst = h_src - h_kernel+1;
+            ws.w_dst = w_src - w_kernel+1;
+        }
+        break;
+    case FFTW_CIRCULAR_SAME:
+        // Circular convolution, modulo N
+        // We don't padd with zeros because if we do, we need to padd with at least h_kernel/2; w_kernel/2 elements
+        // plus the wrapp around
+        // which in facts leads to too much computations compared to the gain obtained with the optimal size
+        ws.h_fftw = h_src;
+        ws.w_fftw = w_src;
+        ws.h_dst = h_src;
+        ws.w_dst = w_src;
+        break;
+    case FFTW_CIRCULAR_SAME_SHIFTED:
+        // Circular convolution, modulo N, shifted by M/2
+        // We don't padd with zeros because if we do, we need to padd with at least h_kernel/2; w_kernel/2 elements
+        // plus the wrapp around
+        // which in facts leads to too much computations compared to the gain obtained with the optimal size
+        ws.h_fftw = h_src;
+        ws.w_fftw = w_src;
+        ws.h_dst = h_src;
+        ws.w_dst = w_src;
+        break;
+    case FFTW_CIRCULAR_CUSTOM:
+        // We here want to compute a circular convolution modulo h_dst, w_dst
+        // These two variables must have been set before calling init_workscape !!
+        ws.h_fftw = ws.h_dst;
+        ws.w_fftw = ws.w_dst;
+        break;
+    default:
+        printf("Unrecognized convolution mode, possible modes are :\n");
+        printf("   - FFTW_LINEAR_FULL \n");
+        printf("   - FFTW_LINEAR_SAME \n");
+        printf("   - FFTW_LINEAR_SAME_UNPADDED\n");
+        printf("   - FFTW_LINEAR_VALID \n");
+        printf("   - FFTW_CIRCULAR_SAME \n");
+        printf("   - FFTW_CIRCULAR_SHIFTED\n");
+        printf("   - FFTW_CIRCULAR_CUSTOM\n");
+    }
+
+    ws.in_src = new double[ws.h_fftw * ws.w_fftw];
+    ws.out_src = (double*) fftw_malloc(sizeof(fftw_complex) * ws.h_fftw * (ws.w_fftw/2+1));
+    ws.in_kernel = new double[ws.h_fftw * ws.w_fftw];
+    ws.out_kernel = (double*) fftw_malloc(sizeof(fftw_complex) * ws.h_fftw * (ws.w_fftw/2+1));
+
+    ws.dst_fft = new double[ws.h_fftw * ws.w_fftw];
+    ws.dst = new double[ws.h_dst * ws.w_dst];
+
+    // Initialization of the plans
+    ws.p_forw_src = fftw_plan_dft_r2c_2d(ws.h_fftw, ws.w_fftw, ws.in_src, (fftw_complex*)ws.out_src, FFTW_ESTIMATE);
+    ws.p_forw_kernel = fftw_plan_dft_r2c_2d(ws.h_fftw, ws.w_fftw, ws.in_kernel, (fftw_complex*)ws.out_kernel, FFTW_ESTIMATE);
+
+    // The backward FFT takes ws.out_kernel as input !!
+    ws.p_back = fftw_plan_dft_c2r_2d(ws.h_fftw, ws.w_fftw, (fftw_complex*)ws.out_kernel, ws.dst_fft, FFTW_ESTIMATE);
+}
+
+void clear_workspace_fftw(FFTW_Workspace & ws)
+{
+    delete[] ws.in_src;
+    fftw_free((fftw_complex*)ws.out_src);
+    delete[] ws.in_kernel;
+    fftw_free((fftw_complex*)ws.out_kernel);
+
+    delete[] ws.dst_fft;
+    delete[] ws.dst;
+
+    // Destroy the plans
+    fftw_destroy_plan(ws.p_forw_src);
+    fftw_destroy_plan(ws.p_forw_kernel);
+    fftw_destroy_plan(ws.p_back);
+}
+
+// Compute the circular convolution of src and kernel modulo ws.h_fftw, ws.w_fftw
+// using the Fast Fourier Transform
+// The result is in ws.dst
+void fftw_circular_convolution(FFTW_Workspace &ws, double * src, double * kernel)
+{
+    double * ptr, *ptr_end, *ptr2;
+
+    // Reset the content of ws.in_src
+    for(ptr = ws.in_src, ptr_end = ws.in_src + ws.h_fftw*ws.w_fftw ; ptr != ptr_end ; ++ptr)
+        *ptr = 0.0;
+    for(ptr = ws.in_kernel, ptr_end = ws.in_kernel + ws.h_fftw*ws.w_fftw ; ptr != ptr_end ; ++ptr)
+        *ptr = 0.0;
+
+    // Then we build our periodic signals
+    //ptr = src;
+    for(int i = 0 ; i < ws.h_src ; ++i)
+        for(int j = 0 ; j < ws.w_src ; ++j, ++ptr)
+            ws.in_src[(i%ws.h_fftw)*ws.w_fftw+(j%ws.w_fftw)] += src[i*ws.w_src + j];
+    //ptr = kernel;
+    for(int i = 0 ; i < ws.h_kernel ; ++i)
+        for(int j = 0 ; j < ws.w_kernel ; ++j, ++ptr)
+            ws.in_kernel[(i%ws.h_fftw)*ws.w_fftw+(j%ws.w_fftw)] += kernel[i*ws.w_kernel + j];
+
+    // And we compute their packed FFT
+    fftw_execute(ws.p_forw_src);
+    fftw_execute(ws.p_forw_kernel);
+
+    // Compute the element-wise product on the packed terms
+    // Let's put the element wise products in ws.in_kernel
+    double re_s, im_s, re_k, im_k;
+    for(ptr = ws.out_src, ptr2 = ws.out_kernel, ptr_end = ws.out_src+2*ws.h_fftw * (ws.w_fftw/2+1); ptr != ptr_end ; ++ptr, ++ptr2)
+    {
+        re_s = *ptr;
+        im_s = *(++ptr);
+        re_k = *ptr2;
+        im_k = *(++ptr2);
+        *(ptr2-1) = re_s * re_k - im_s * im_k;
+        *ptr2 = re_s * im_k + im_s * re_k;
+    }
+
+    // Compute the backward FFT
+    // Carefull, The backward FFT does not preserve the output
+    fftw_execute(ws.p_back);
+    // Scale the transform
+    for(ptr = ws.dst_fft, ptr_end = ws.dst_fft + ws.w_fftw*ws.h_fftw ; ptr != ptr_end ; ++ptr)
+        *ptr /= double(ws.h_fftw*ws.w_fftw);
+
+    // That's it !
+}
+
+void fftw_convolve(FFTW_Workspace &ws, double * src,double * kernel)
+{
+    if(ws.h_fftw <= 0 || ws.w_fftw <= 0)
+        return;
+
+    // Compute the circular convolution
+    fftw_circular_convolution(ws, src, kernel);
+
+    // Depending on the type of convolution one is looking for, we extract the appropriate part of the result from out_src
+    int h_offset, w_offset;
+
+//    double * dst_ptr;
+//    double * src_ptr;
+    switch(ws.mode)
+    {
+    case FFTW_LINEAR_FULL:
+        // Full Linear convolution
+        // Here we just keep the first [0:h_dst-1 ; 0:w_dst-1] real part elements of out_src
+        for(int i = 0 ; i < ws.h_dst  ; ++i)
+            memcpy(&ws.dst[i*ws.w_dst], &ws.dst_fft[i*ws.w_fftw], ws.w_dst*sizeof(double));
+        break;
+    case FFTW_LINEAR_SAME_UNPADDED:
+        // Same linear convolution
+        // Here we just keep the first [h_filt/2:h_filt/2+h_dst-1 ; w_filt/2:w_filt/2+w_dst-1] real part elements of out_src
+        h_offset = int(ws.h_kernel/2.0);
+        w_offset = int(ws.w_kernel/2.0);
+        for(int i = 0 ; i < ws.h_dst ; ++i)
+            memcpy(&ws.dst[i*ws.w_dst], &ws.dst_fft[(i+h_offset)*ws.w_fftw+w_offset], ws.w_dst*sizeof(double));
+            //for(int j = 0 ; j < ws.w_dst ; ++j)
+             //   ws.dst[i*ws.w_dst + j] = ws.out_src[2*((i+h_offset)*ws.w_fftw+j+w_offset)+0]/double(ws.w_fftw * ws.h_fftw);
+        break;
+    case FFTW_LINEAR_SAME:
+        // Same linear convolution
+        // Here we just keep the first [h_filt/2:h_filt/2+h_dst-1 ; w_filt/2:w_filt/2+w_dst-1] real part elements of out_src
+        h_offset = int(ws.h_kernel/2.0);
+        w_offset = int(ws.w_kernel/2.0);
+        for(int i = 0 ; i < ws.h_dst ; ++i)
+            memcpy(&ws.dst[i*ws.w_dst], &ws.dst_fft[(i+h_offset)*ws.w_fftw+w_offset], ws.w_dst*sizeof(double));
+        break;
+    case FFTW_LINEAR_VALID:
+        // Valid linear convolution
+        // Here we just take [h_dst x w_dst] elements starting at [h_kernel-1;w_kernel-1]
+        h_offset = ws.h_kernel - 1;
+        w_offset = ws.w_kernel - 1;
+        for(int i = 0 ; i < ws.h_dst ; ++i)
+            memcpy(&ws.dst[i*ws.w_dst], &ws.dst_fft[(i+h_offset)*ws.w_fftw+w_offset], ws.w_dst*sizeof(double));
+        break;
+    case FFTW_CIRCULAR_SAME:
+        // Circular convolution
+        // We copy the first [0:h_dst-1 ; 0:w_dst-1] real part elements of out_src
+        for(int i = 0 ; i < ws.h_dst ; ++i)
+            memcpy(&ws.dst[i*ws.w_dst], &ws.dst_fft[i*ws.w_fftw], ws.w_dst*sizeof(double) );
+        break;
+    case FFTW_CIRCULAR_SAME_SHIFTED:
+        // Shifted Circular convolution
+        // We copy the [h_kernel/2:h_kernel/2+h_dst-1 ; w_kernel/2:w_kernel/2+w_dst-1] real part elements of out_src
+        for(int i = 0 ; i < ws.h_dst ; ++i)
+            for(int j = 0 ; j < ws.w_dst ; ++j)
+                ws.dst[i*ws.w_dst + j] = ws.dst_fft[((i+int(ws.h_kernel/2.0))%ws.h_fftw)*ws.w_fftw+(j+int(ws.w_kernel/2.0))%ws.w_fftw];
+        break;
+    case FFTW_CIRCULAR_CUSTOM:
+        for(int i = 0 ; i < ws.h_dst ; ++i)
+            memcpy(&ws.dst[i*ws.w_dst], &ws.dst_fft[i*ws.w_fftw], ws.w_dst*sizeof(double) );
+        break;
+    default:
+        printf("Unrecognized convolution mode, possible modes are :\n");
+        printf("   - FFTW_LINEAR_FULL \n");
+        printf("   - FFTW_LINEAR_SAME \n");
+        printf("   - FFTW_LINEAR_SAME_UNPADDED\n");
+        printf("   - FFTW_LINEAR_VALID \n");
+        printf("   - FFTW_CIRCULAR_SAME \n");
+        printf("   - FFTW_CIRCULAR_SHIFTED\n");
+        printf("   - FFTW_CIRCULAR_CUSTOM\n");
+    }
+}
+
diff --git a/Core/src/Exceptions.cpp b/Core/src/Exceptions.cpp
index 72b7e5bb4a3..70350420c1c 100644
--- a/Core/src/Exceptions.cpp
+++ b/Core/src/Exceptions.cpp
@@ -60,6 +60,12 @@ LogicErrorException::LogicErrorException(const std::string &message)
     LogExceptionMessage(message);
 }
 
+RuntimeErrorException::RuntimeErrorException(const std::string &message)
+    : std::runtime_error(message)
+{
+    LogExceptionMessage(message);
+}
+
 DivisionByZeroException::DivisionByZeroException(const std::string &message)
     : std::runtime_error(message)
 {
diff --git a/Core/src/IFunctionalTest.cpp b/Core/src/IFunctionalTest.cpp
index 8da8babfa99..8946990022e 100644
--- a/Core/src/IFunctionalTest.cpp
+++ b/Core/src/IFunctionalTest.cpp
@@ -8,6 +8,6 @@ IFunctionalTest::IFunctionalTest()
 
 void IFunctionalTest::execute()
 {
-    throw NotImplementedException("This test can't run.");
+    throw NotImplementedException("This test can't run. Undefined execute method.");
 }
 
diff --git a/Core/src/MathFunctions.cpp b/Core/src/MathFunctions.cpp
index f9961a4a986..f0cb5848bd0 100644
--- a/Core/src/MathFunctions.cpp
+++ b/Core/src/MathFunctions.cpp
@@ -88,3 +88,23 @@ std::vector<complex_t > MathFunctions::FastFourierTransform(const std::vector<do
 }
 
 
+/* ************************************************************************* */
+// convolution of two real vectors of equal size
+/* ************************************************************************* */
+std::vector<complex_t> MathFunctions::ConvolveFFT(const std::vector<double> &signal, const std::vector<double> &resfunc)
+{
+    if(signal.size() != resfunc.size() ) {
+        throw std::runtime_error("MathFunctions::ConvolveFFT() -> This convolution works only for two vectors of equal size. Use Convolve class instead.");
+    }
+    std::vector<complex_t > fft_signal = MathFunctions::FastFourierTransform(signal, MathFunctions::ForwardFFT);
+    std::vector<complex_t > fft_resfunc = MathFunctions::FastFourierTransform(resfunc, MathFunctions::ForwardFFT);
+
+    std::vector<complex_t > fft_prod;
+    fft_prod.resize(fft_signal.size());
+    for(size_t i=0; i<fft_signal.size(); i++){
+        fft_prod[i] = fft_signal[i] * fft_resfunc[i];
+    }
+
+    std::vector<complex_t > result = MathFunctions::FastFourierTransform(fft_prod, MathFunctions::BackwardFFT);
+    return result;
+}
diff --git a/GISASFW.pro b/GISASFW.pro
index 9a8faa9233f..c2414637001 100644
--- a/GISASFW.pro
+++ b/GISASFW.pro
@@ -3,10 +3,11 @@ TEMPLATE = subdirs
 SUBDIRS += \
     Core \
     ThirdParty/gtest \
+#    ThirdParty/fftw++ \
     App \
     UnitTests/TestCore
 
-TestCore.depends = ThirdParty/gtest
+TestCore.depends = ThirdParty/gtest ThirdParty/fftw++
 
 # means that compilation will be in the listed order
 CONFIG += ordered
-- 
GitLab