From a96fdc6b6c719b0e3cfa02bc3e43f1cfe95e5265 Mon Sep 17 00:00:00 2001
From: Tammo Jan Dijkema <dijkema@astron.nl>
Date: Wed, 19 Apr 2017 11:20:59 +0000
Subject: [PATCH] Task #10121: reintegrate task branch: experimental direction
 dependent solver in DPPP

---
 .gitattributes                                |  19 +
 CEP/DP3/CMakeLists.txt                        |   6 +
 CEP/DP3/DPPP_DDECal/CMakeLists.txt            |  11 +
 .../include/DPPP_DDECal/CMakeLists.txt        |  12 +
 .../include/DPPP_DDECal/Constraint.h          | 137 +++++
 .../DPPP_DDECal/include/DPPP_DDECal/DDECal.h  | 147 +++++
 .../include/DPPP_DDECal/KLFitter.h            |  36 ++
 .../include/DPPP_DDECal/PiercePoint.h         |  41 ++
 .../include/DPPP_DDECal/Register.h            |  37 ++
 .../include/DPPP_DDECal/ScreenConstraint.h    |  40 ++
 .../include/DPPP_DDECal/multidirsolver.h      |  79 +++
 .../include/DPPP_DDECal/screenfitter.h        |  45 ++
 CEP/DP3/DPPP_DDECal/src/CMakeLists.txt        |  11 +
 CEP/DP3/DPPP_DDECal/src/Constraint.cc         | 131 ++++
 CEP/DP3/DPPP_DDECal/src/DDECal.cc             | 557 ++++++++++++++++++
 CEP/DP3/DPPP_DDECal/src/KLFitter.cc           |  43 ++
 CEP/DP3/DPPP_DDECal/src/PiercePoint.cc        |  50 ++
 CEP/DP3/DPPP_DDECal/src/Register.cc           |  36 ++
 CEP/DP3/DPPP_DDECal/src/ScreenConstraint.cc   | 158 +++++
 CEP/DP3/DPPP_DDECal/src/multidirsolver.cc     | 209 +++++++
 CEP/DP3/DPPP_DDECal/src/screenfitter.cc       |   8 +
 CMake/LofarPackageList.cmake                  |   1 +
 22 files changed, 1814 insertions(+)
 create mode 100644 CEP/DP3/DPPP_DDECal/CMakeLists.txt
 create mode 100644 CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/CMakeLists.txt
 create mode 100644 CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/Constraint.h
 create mode 100644 CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/DDECal.h
 create mode 100644 CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/KLFitter.h
 create mode 100644 CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/PiercePoint.h
 create mode 100644 CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/Register.h
 create mode 100644 CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/ScreenConstraint.h
 create mode 100644 CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/multidirsolver.h
 create mode 100644 CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/screenfitter.h
 create mode 100644 CEP/DP3/DPPP_DDECal/src/CMakeLists.txt
 create mode 100644 CEP/DP3/DPPP_DDECal/src/Constraint.cc
 create mode 100644 CEP/DP3/DPPP_DDECal/src/DDECal.cc
 create mode 100644 CEP/DP3/DPPP_DDECal/src/KLFitter.cc
 create mode 100644 CEP/DP3/DPPP_DDECal/src/PiercePoint.cc
 create mode 100644 CEP/DP3/DPPP_DDECal/src/Register.cc
 create mode 100644 CEP/DP3/DPPP_DDECal/src/ScreenConstraint.cc
 create mode 100644 CEP/DP3/DPPP_DDECal/src/multidirsolver.cc
 create mode 100644 CEP/DP3/DPPP_DDECal/src/screenfitter.cc

diff --git a/.gitattributes b/.gitattributes
index 7c3febba9f2..99b198590cd 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -932,6 +932,25 @@ CEP/DP3/DPPP_AOFlag/test/CMakeLists.txt -text
 CEP/DP3/DPPP_AOFlag/test/tAOFlaggerStep.cc -text
 CEP/DP3/DPPP_AOFlag/test/tAOFlaggerStep.run -text
 CEP/DP3/DPPP_AOFlag/test/tAOFlaggerStep.sh -text
+CEP/DP3/DPPP_DDECal/CMakeLists.txt -text
+CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/CMakeLists.txt -text
+CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/Constraint.h -text
+CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/DDECal.h -text
+CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/KLFitter.h -text
+CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/PiercePoint.h -text
+CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/Register.h -text
+CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/ScreenConstraint.h -text
+CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/multidirsolver.h -text
+CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/screenfitter.h -text
+CEP/DP3/DPPP_DDECal/src/CMakeLists.txt -text
+CEP/DP3/DPPP_DDECal/src/Constraint.cc -text
+CEP/DP3/DPPP_DDECal/src/DDECal.cc -text
+CEP/DP3/DPPP_DDECal/src/KLFitter.cc -text
+CEP/DP3/DPPP_DDECal/src/PiercePoint.cc -text
+CEP/DP3/DPPP_DDECal/src/Register.cc -text
+CEP/DP3/DPPP_DDECal/src/ScreenConstraint.cc -text
+CEP/DP3/DPPP_DDECal/src/multidirsolver.cc -text
+CEP/DP3/DPPP_DDECal/src/screenfitter.cc -text
 CEP/DP3/PythonDPPP/CMakeLists.txt -text
 CEP/DP3/PythonDPPP/include/PythonDPPP/CMakeLists.txt -text
 CEP/DP3/PythonDPPP/include/PythonDPPP/DPStepBase.h -text
diff --git a/CEP/DP3/CMakeLists.txt b/CEP/DP3/CMakeLists.txt
index 7c693c95b83..2ef1a8b00ff 100644
--- a/CEP/DP3/CMakeLists.txt
+++ b/CEP/DP3/CMakeLists.txt
@@ -7,3 +7,9 @@ lofar_add_package(DPPP_AOFlag)
 lofar_add_package(SPW_Combine SPWCombine)
 lofar_add_package(AOFlagger)
 
+lofar_find_package(Armadillo)
+if(${ARMADILLO_FOUND})
+  lofar_add_package(DPPP_DDECal)
+else()
+  message(WARNING "Armadillo was not found, NOT building DPPP_DDECal")
+endif()
diff --git a/CEP/DP3/DPPP_DDECal/CMakeLists.txt b/CEP/DP3/DPPP_DDECal/CMakeLists.txt
new file mode 100644
index 00000000000..4ebe58fb959
--- /dev/null
+++ b/CEP/DP3/DPPP_DDECal/CMakeLists.txt
@@ -0,0 +1,11 @@
+# $Id: CMakeLists.txt 27640 2013-12-04 08:02:49Z diepen $
+
+lofar_package(DPPP_DDECal 1.0 DEPENDS DPPP)
+
+include(LofarFindPackage)
+lofar_find_package(Casacore COMPONENTS casa ms tables REQUIRED)
+lofar_find_package(Armadillo REQUIRED)
+
+add_subdirectory(include/DPPP_DDECal)
+add_subdirectory(src)
+# add_subdirectory(test)
diff --git a/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/CMakeLists.txt b/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/CMakeLists.txt
new file mode 100644
index 00000000000..4abee5ccfdd
--- /dev/null
+++ b/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/CMakeLists.txt
@@ -0,0 +1,12 @@
+# $Id: CMakeLists.txt 30990 2015-02-12 12:27:47Z diepen $
+
+# List of header files that will be installed.
+set(inst_HEADERS Register.h DDECal.h multidirsolver.h H5Parm.h Constraint.h ScreenConstraint.h PiercePoint.h)
+
+# Create symbolic link to include directory.
+execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink
+  ${CMAKE_CURRENT_SOURCE_DIR}
+  ${CMAKE_BINARY_DIR}/include/${PACKAGE_NAME})
+
+# Install header files.
+#install(FILES ${inst_HEADERS} DESTINATION include/${PACKAGE_NAME})
diff --git a/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/Constraint.h b/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/Constraint.h
new file mode 100644
index 00000000000..e3a42b03ebe
--- /dev/null
+++ b/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/Constraint.h
@@ -0,0 +1,137 @@
+#ifndef CONSTRAINT_H
+#define CONSTRAINT_H
+
+#ifdef AOPROJECT
+#include "phasefitter.h"
+#define UPTR std::unique_ptr
+#else
+#include <DPPP/phasefitter.h>
+#define UPTR std::auto_ptr
+#endif
+
+#include <complex>
+#include <memory>
+#include <set>
+#include <vector>
+
+/**
+ * This class is the base class for classes that implement a constraint on
+ * calibration solutions. Constraints are used to increase
+ * the converge of calibration by applying these inside the solving step.
+ * 
+ * The MultiDirSolver class uses this class for constrained calibration.
+ */
+class Constraint
+{
+public:
+  typedef std::complex<double> dcomplex;
+  struct Result
+  {
+  public:
+    std::vector<double> vals;
+    std::string axes; // Comma-separated string with axes names, fastest varying last
+    std::vector<size_t> dims;
+    std::string name;
+  };
+
+  virtual ~Constraint() { }
+   
+  /**
+   * TODO To be removed: constraints should have their own init specific initialization methods.
+   */
+  virtual void init(size_t nAntennas, size_t nDirections, 
+                    size_t nChannelBlocks, const double* frequencies) = 0;
+
+  /**
+   * This method applies the constraints to the solutions.
+   * @param solutions is an array of array, such that:
+   * - solutions[ch] is a pointer for channelblock ch to antenna x directions solutions.
+   * - directions is the dimension with the fastest changing index.
+   * @param time Central time of interval.
+   */
+  virtual std::vector<Result> Apply(
+    std::vector<std::vector<dcomplex> >& solutions,
+    double time) = 0;
+};
+
+/**
+ * Awkwardly named, this class constraints the amplitudes of the solution to be unity, but
+ * keeps the phase.
+ */
+class PhaseConstraint : public Constraint
+{
+public:
+  PhaseConstraint() {};
+
+  virtual void init(size_t, size_t, size_t, const double*) {};
+
+  virtual std::vector<Result> Apply(
+                    std::vector<std::vector<dcomplex> >& solutions,
+                    double time);
+};
+
+class TECConstraint : public Constraint
+{
+public:
+  enum Mode {
+    /** Solve for both a (differential) TEC and an XX/YY-common scalar */
+    TECAndCommonScalarMode,
+    /** Solve only for a (differential) TEC value */
+    TECOnlyMode
+  };
+  
+  TECConstraint(Mode mode, size_t nAntennas, size_t nDirections, 
+                size_t nChannelBlocks, const double* frequencies);
+  TECConstraint(Mode mode);
+
+  virtual void init(size_t nAntennas, size_t nDirections, 
+                    size_t nChannelBlocks, const double* frequencies);
+  
+  virtual std::vector<Result> Apply(
+                    std::vector<std::vector<dcomplex> >& solutions,
+                       double time);
+  
+private:
+  Mode _mode;
+  size_t _nAntennas, _nDirections, _nChannelBlocks;
+  std::vector<PhaseFitter> _phaseFitters;
+};
+
+/**
+ * This constraint limits averages the solutions of a given list of antennas,
+ * so that they have equal solutions.
+ * 
+ * The DDE solver uses this constraint to average the solutions of the core
+ * antennas. Core antennas are there defined by a given maximum distance from
+ * a reference antenna. In that case, the reference antenna is by default the first
+ * antenna. This constraint is meant to force all core stations to
+ * have the same solution, thereby decreasing the noise in their solutions.
+ */
+class CoreConstraint : public Constraint
+{
+public:
+  CoreConstraint() { }
+
+  virtual void init(size_t nAntennas, size_t nDirections, 
+                    size_t nChannelBlocks, const double*)
+  {
+    _nAntennas = nAntennas;
+    _nDirections = nDirections;
+    _nChannelBlocks = nChannelBlocks;
+  }
+                     
+  void setCoreAntennas(const std::set<size_t>& coreAntennas)
+  {
+    _coreAntennas = coreAntennas;
+  }
+  
+  virtual std::vector<Result> Apply(
+                    std::vector<std::vector<dcomplex> >& solutions,
+                    double time);
+  
+private:
+  size_t _nAntennas, _nDirections, _nChannelBlocks;
+  std::set<size_t> _coreAntennas;
+};
+
+#endif
diff --git a/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/DDECal.h b/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/DDECal.h
new file mode 100644
index 00000000000..319660eeca6
--- /dev/null
+++ b/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/DDECal.h
@@ -0,0 +1,147 @@
+//# DDE.h: DPPP step class to calibrate direction dependent gains
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id: DDECal.h 21598 2012-07-16 08:07:34Z diepen $
+//#
+//# @author Tammo Jan Dijkema
+
+#ifndef DPPP_DDECAL_H
+#define DPPP_DDECAL_H
+
+// @file
+// @brief DPPP step class to apply a calibration correction to the data
+
+#include <DPPP/DPInput.h>
+#include <DPPP/DPBuffer.h>
+#include <DPPP/H5Parm.h>
+#include <DPPP/BaselineSelection.h>
+#include <DPPP/Patch.h>
+#include <DPPP/Predict.h>
+#include <DPPP/SourceDBUtil.h>
+#include <DPPP/ApplyBeam.h>
+#include <DPPP_DDECal/multidirsolver.h>
+#include <DPPP_DDECal/Constraint.h>
+#include <StationResponse/Station.h>
+#include <StationResponse/Types.h>
+#include <ParmDB/Parm.h>
+#include <casa/Arrays/Cube.h>
+#include <casa/Quanta/MVEpoch.h>
+#include <measures/Measures/MEpoch.h>
+#include <casa/Arrays/ArrayMath.h>
+
+namespace LOFAR {
+
+  class ParameterSet;
+
+  namespace DPPP {
+    // @ingroup NDPPP
+
+    // This class is a DPStep class to calibrate (direction independent) gains.
+
+    typedef vector<Patch::ConstPtr> PatchList;
+    typedef std::pair<size_t, size_t >Baseline;
+
+    class DDECal: public DPStep
+    {
+    public:
+      // Construct the object.
+      // Parameters are obtained from the parset using the given prefix.
+      DDECal (DPInput*, const ParameterSet&, const string& prefix);
+
+      virtual ~DDECal();
+
+      // Create an DDECal object using the given parset.
+      static DPStep::ShPtr makeStep (DPInput*, const ParameterSet&,
+                                     const std::string&);
+
+      // Process the data.
+      // It keeps the data.
+      // When processed, it invokes the process function of the next step.
+      virtual bool process (const DPBuffer&);
+
+      // Initialize H5parm-file
+      void initH5parm();
+
+      // Write out the solutions
+      void writeSolutions();
+
+      // Finish the processing of this step and subsequent steps.
+      virtual void finish();
+
+      // Update the general info.
+      virtual void updateInfo (const DPInfo&);
+
+      // Show the step parameters.
+      virtual void show (std::ostream&) const;
+
+      // Show the timings.
+      virtual void showTimings (std::ostream&, double duration) const;
+
+
+    private:
+      // Initialize the parmdb
+      void initH5Parm();
+
+      //# Data members.
+      DPInput*         itsInput;
+      string           itsName;
+      vector<DPBuffer> itsBufs;
+
+      // The time of the current buffer (in case of solint, average time)
+      double           itsAvgTime;
+      std::vector<casa::Complex*> itsDataPtrs;
+
+      // For each timeslot, a vector of nDir buffers
+      std::vector<std::vector<casa::Complex*> > itsModelDataPtrs;
+
+      // For each time, for each channel block, a vector of size nAntennas * nDirections
+      std::vector<std::vector<std::vector<casa::DComplex> > > itsSols;
+
+      // For each time, for each constraint, a vector of results (e.g. tec and phase)
+      std::vector<std::vector<std::vector<Constraint::Result> > > itsConstraintSols;
+
+      casa::Cube<casa::Complex> itsModelData;
+      string           itsH5ParmName;
+      H5Parm           itsH5Parm;
+
+      string           itsMode;
+      uint             itsTimeStep;
+      uint             itsSolInt;
+      uint             itsStepInSolInt;
+      uint             itsNChan;
+      uint             itsNFreqCells;
+      vector<vector<string> > itsDirections; // For each direction, a vector of patches
+      vector<casa::CountedPtr<Constraint> > itsConstraints;
+
+      vector<Predict>     itsPredictSteps;
+      vector<MultiResultStep::ShPtr> itsResultSteps; // For each directions, a multiresultstep with all times
+
+      NSTimer          itsTimer;
+      NSTimer          itsTimerPredict;
+      NSTimer          itsTimerSolve;
+      NSTimer          itsTimerWrite;
+      double           itsCoreConstraint;
+
+      MultiDirSolver   itsMultiDirSolver;
+    };
+
+  } //# end namespace
+}
+
+#endif
diff --git a/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/KLFitter.h b/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/KLFitter.h
new file mode 100644
index 00000000000..301d5686afa
--- /dev/null
+++ b/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/KLFitter.h
@@ -0,0 +1,36 @@
+#ifndef KLFITTER_H
+#define KLFITTER_H
+#include<vector>
+#include <armadillo>
+#include <DPPP_DDECal/PiercePoint.h>
+
+using namespace arma;
+
+class KLFitter
+{//creates KH base and fits screens from collection of PiercePoints
+public:
+  KLFitter(double r0=1000.,double beta=5./3.,int order=9);
+  void calculateCorrMatrix(const vector<PiercePoint> pp);
+  void doFit();
+  size_t getOrder() const {return itsOrder;}
+  double* PhaseData() { return _phases.memptr(); }
+  double* WData() { return _weights.memptr(); }
+  double* ParData() { return itsPar.memptr(); }
+  double* TECFitWhiteData() { return itsTECFitWhite.memptr(); }
+  double* PPData() { return itsPiercePoints.memptr(); }
+
+private:
+  size_t                  itsOrder;
+  double                  itsR0,itsBeta;
+  Mat<double>             itsPiercePoints;
+  Col<double>             _phases;
+  Mat<double>             _weights; //weights of the data points
+  Mat<double>             itsCorrMatrix; //Correlation Matrix for KL fit
+  Mat<double>             itsinvC;  //for quick interpolation
+  Mat<double>             itsU;
+  Mat<double>             itsinvU;
+  Mat<double>             itsTECFitWhite;
+  Col<double>             itsPar;
+};
+
+#endif
diff --git a/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/PiercePoint.h b/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/PiercePoint.h
new file mode 100644
index 00000000000..a39ef99a99b
--- /dev/null
+++ b/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/PiercePoint.h
@@ -0,0 +1,41 @@
+#ifndef PIERCEPOINT_H
+#define PIERCEPOINT_H
+
+#include <vector>
+#include <measures/Measures/MDirection.h>
+#include <measures/Measures/MCDirection.h>
+#include <measures/Measures/MCPosition.h>
+#include <measures/Measures/MPosition.h>
+#include <measures/Measures/MeasConvert.h>
+#include <measures/Measures/MEpoch.h>
+
+#include <armadillo>
+
+using namespace arma;
+
+class PiercePoint 
+{
+public:
+  PiercePoint();
+  PiercePoint(const casa::MPosition &ant,const casa::MDirection &source,const double height);
+  PiercePoint(const casa::MPosition &ant,const casa::MDirection &source);
+  void init(const casa::MPosition &ant,const casa::MDirection &source,const double height);
+  void evaluate(casa::MEpoch time);
+  Col<double>  getValue() const {return itsValue;} 
+  casa::MPosition  getPos() const {return itsPosition;}
+  casa::MDirection  getDir() const {return itsDirection;}
+private:
+  static const double IONOheight; //default height in meter
+  static const double EarthRadius; //default Earth radius in meter
+  //station position
+  casa::MPosition     itsPosition;
+  //source position
+  casa::MDirection     itsDirection;
+  // Ionospheric layer height.
+  double              itsIonoHeight;
+  //  square of length antenna vector (int ITRF) minus square of vector to piercepoint. This is constant for a assumed spherical Earth
+  double              itsC;
+  Col<double>         itsValue; //PiercePoint in ITRF coordinates
+};
+
+#endif
diff --git a/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/Register.h b/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/Register.h
new file mode 100644
index 00000000000..8144c212546
--- /dev/null
+++ b/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/Register.h
@@ -0,0 +1,37 @@
+//# Register.h: Register AOFlag steps in DPPP
+//# Copyright (C) 2015
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id: AORFlagger.h 26900 2013-10-08 20:12:58Z loose $
+//#
+//# @author Ger van Diepen
+
+#ifndef DPPP_DDECAL_REGISTER_H
+#define DPPP_DDECAL_REGISTER_H
+
+// @file
+// @brief Register AOFlag steps in DPPP
+
+
+// Define the function (without name mangling) to register the 'constructor'.
+extern "C"
+{
+  void register_ddecal();
+}
+
+#endif
diff --git a/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/ScreenConstraint.h b/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/ScreenConstraint.h
new file mode 100644
index 00000000000..6683a99582f
--- /dev/null
+++ b/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/ScreenConstraint.h
@@ -0,0 +1,40 @@
+#ifndef SCREEN_CONSTRAINT_H
+#define SCREEN_CONSTRAINT_H
+
+#include <DPPP_DDECal/multidirsolver.h>
+#include <DPPP_DDECal/PiercePoint.h>
+#include <DPPP_DDECal/KLFitter.h>
+
+#include <cmath>
+#include <vector>
+#include <memory>
+
+class ScreenConstraint : public Constraint
+{
+public:
+  ScreenConstraint();
+  virtual void init(size_t nAntennas, size_t nDirections, 
+                    size_t nChannelBlocks, const double* frequencies);
+
+  virtual std::vector<Constraint::Result> Apply(std::vector<std::vector<MultiDirSolver::DComplex> >& solutions,double time);
+  virtual void CalculatePiercepoints();
+
+  void setAntennaPositions(const std::vector<std::vector<double> > antenna_pos);
+  void setDirections(const std::vector<std::pair<double, double> > source_pos);
+  void setTime(double time);
+  void initPiercePoints();
+
+private:
+  size_t _nAntennas, _nDirections, _nChannelBlocks;
+  std::vector<std::vector<double> > itsAntennaPos;
+  std::vector<std::vector<double> > itsSourcePos;
+  std::vector<double>               itsFrequencies;
+  // antenna positions
+  // source positions
+  // measures instance ofzo               
+  std::vector<std::vector<PiercePoint> > itsPiercePoints;  //temporary hold calculated piercepoints per antenna
+  std::vector<KLFitter> _screenFitters;
+  double itsCurrentTime;
+};
+
+#endif
diff --git a/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/multidirsolver.h b/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/multidirsolver.h
new file mode 100644
index 00000000000..32af51b3c36
--- /dev/null
+++ b/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/multidirsolver.h
@@ -0,0 +1,79 @@
+#ifndef MULTI_DIR_SOLVER_H
+#define MULTI_DIR_SOLVER_H
+
+#ifdef AOPROJECT
+#include "phasefitter.h"
+#include "Constraint.h"
+#define UPTR std::unique_ptr
+#else
+#include <DPPP/phasefitter.h>
+#include <DPPP_DDECal/Constraint.h>
+#define UPTR std::auto_ptr
+#endif
+
+#include <armadillo>
+
+#include <complex>
+#include <vector>
+#include <memory>
+
+class MultiDirSolver
+{
+public:
+  typedef std::complex<double> DComplex;
+  typedef std::complex<float> Complex;
+  
+  struct SolveResult {
+    size_t iterations;
+    std::vector<std::vector<Constraint::Result> > _results;
+  };
+  
+  enum CalibrationMode { CalibrateComplexGain, 
+                         CalibrateTEC1, 
+                         CalibrateTEC2, 
+                         CalibratePhase };
+  
+  MultiDirSolver(size_t maxIterations, double accuracy, double stepSize);
+  
+  void init(size_t nAntennas, size_t nDirections, size_t nChannels, 
+            const std::vector<int>& ant1, const std::vector<int>& ant2);
+  
+  // data[i] is een pointer naar de data voor tijdstap i, vanaf die pointer staat het in volgorde als in MS (bl, chan, pol)
+  // mdata[i] is een pointer voor tijdstap i naar arrays van ndir model data pointers (elk van die data pointers staat in zelfde volgorde als data)
+  // solutions[ch] is een pointer voor channelblock ch naar antenna x directions oplossingen.
+  SolveResult process(std::vector<Complex*>& data, std::vector<std::vector<Complex* > >& modelData,
+    std::vector<std::vector<DComplex> >& solutions, double time) const;
+  
+  void set_mode(CalibrationMode mode) { _mode = mode; }
+  
+  void set_channel_blocks(size_t nChannelBlocks) { _nChannelBlocks = nChannelBlocks; }
+  
+  void set_max_iterations(size_t maxIterations) { _maxIterations = maxIterations; }
+  
+  void set_accuracy(double accuracy) { _accuracy = accuracy; }
+  
+  void set_step_size(double stepSize) { _stepSize = stepSize; }
+  
+  void add_constraint(Constraint* constraint) { _constraints.push_back(constraint); }
+  
+private:
+  void performSolveIteration(size_t channelBlockIndex,
+                             std::vector<arma::cx_mat>& gTimesCs,
+                             std::vector<arma::cx_vec>& vs,
+                             const std::vector<DComplex>& solutions,
+                             std::vector<DComplex>& nextSolutions,
+                             const std::vector<Complex *>& data,
+                             const std::vector<std::vector<Complex *> >& modelData) const;
+  
+  size_t _nAntennas, _nDirections, _nChannels, _nChannelBlocks;
+  std::vector<int> _ant1, _ant2;
+  
+  // Calibration setup
+  enum CalibrationMode _mode;
+  size_t _maxIterations;
+  double _accuracy;
+  double _stepSize;
+  std::vector<Constraint*> _constraints;
+};
+
+#endif
diff --git a/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/screenfitter.h b/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/screenfitter.h
new file mode 100644
index 00000000000..b148b1ec114
--- /dev/null
+++ b/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/screenfitter.h
@@ -0,0 +1,45 @@
+//# screenfitter.h: Class to perform screen fitting 
+//# Copyright (C) 2016
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//#
+//# @author Maaijke Mevius
+
+/**
+ * @file screenfitter.h Implements TEC model screen filter @ref ScreenFitter.
+ * @author Maaijke Mevius
+ * @date 2017-02-01
+ */
+
+#ifndef SCREEN_FITTER_H
+#define SCREEN_FITTER_H
+#include <armadillo>
+#include <vector>
+using namespace arma;
+
+class ScreenFitter{
+ public:
+  ScreenFitter();
+  double* PhaseData() { return _phases.data(); }
+
+ private:
+  std::vector<double> _phases,_frequencies, _weights;
+  mat _corrmatrix;  //correlation matrix
+   
+};
+#endif
diff --git a/CEP/DP3/DPPP_DDECal/src/CMakeLists.txt b/CEP/DP3/DPPP_DDECal/src/CMakeLists.txt
new file mode 100644
index 00000000000..0e43f83595f
--- /dev/null
+++ b/CEP/DP3/DPPP_DDECal/src/CMakeLists.txt
@@ -0,0 +1,11 @@
+# $Id: CMakeLists.txt 30439 2014-11-19 15:04:34Z dijkema $
+
+include(LofarPackageVersion)
+
+lofar_add_library(dppp_ddecal
+  Package__Version.cc
+  DDECal.cc Register.cc
+  KLFitter.cc DDECal.cc multidirsolver.cc Constraint.cc ScreenConstraint.cc PiercePoint.cc
+)
+
+lofar_add_bin_program(versiondppp_ddecal versiondppp_ddecal.cc)
diff --git a/CEP/DP3/DPPP_DDECal/src/Constraint.cc b/CEP/DP3/DPPP_DDECal/src/Constraint.cc
new file mode 100644
index 00000000000..53f1abd4177
--- /dev/null
+++ b/CEP/DP3/DPPP_DDECal/src/Constraint.cc
@@ -0,0 +1,131 @@
+#ifdef AOPROJECT
+#include "Constraint.h"
+#include <omp.h> // for tec constraints
+#else
+#include <DPPP_DDECal/Constraint.h>
+#include <Common/OpenMP.h>
+#endif
+
+
+std::vector<Constraint::Result> PhaseConstraint::Apply(
+    std::vector<std::vector<dcomplex> >& solutions, double)
+{
+  for (uint ch=0; ch<solutions.size(); ++ch) {
+    for (uint solIndex=0; solIndex<solutions[ch].size(); ++solIndex) {
+      solutions[ch][solIndex] /= std::abs(solutions[ch][solIndex]);
+    }
+  }
+
+  return std::vector<Constraint::Result>();
+}
+
+TECConstraint::TECConstraint(Mode mode) :
+  _mode(mode),
+  _nAntennas(0)
+  ,_nDirections(0),
+  _nChannelBlocks(0),
+  _phaseFitters()
+{
+}
+
+TECConstraint::TECConstraint(Mode mode, size_t nAntennas, size_t nDirections, 
+                             size_t nChannelBlocks, const double* frequencies) :
+  _mode(mode)
+{
+  init(nAntennas, nDirections, nChannelBlocks, frequencies);
+}
+
+void TECConstraint::init(size_t nAntennas, size_t nDirections, 
+                         size_t nChannelBlocks, const double* frequencies) {
+  _nAntennas = nAntennas;
+  _nDirections = nDirections;
+  _nChannelBlocks = nChannelBlocks;
+  _phaseFitters.resize(
+#ifdef AOPROJECT
+      omp_get_max_threads()
+#else
+      LOFAR::OpenMP::maxThreads()
+#endif
+   );
+
+  for(size_t i=0; i!=_phaseFitters.size(); ++i)
+  {
+    _phaseFitters[i].SetChannelCount(_nChannelBlocks);
+    std::memcpy(_phaseFitters[i].FrequencyData(), frequencies, sizeof(double) * _nChannelBlocks);
+  }
+}
+
+std::vector<Constraint::Result> TECConstraint::Apply(
+    std::vector<std::vector<dcomplex> >& solutions, double)
+{
+  std::vector<Constraint::Result> res(2);
+
+  res[0].vals.resize(_nAntennas*_nDirections);
+  res[0].axes="ant,dir,freq";
+  res[0].name="tec";
+  res[0].dims.resize(3);
+  res[0].dims[0]=_nAntennas;
+  res[0].dims[1]=_nDirections;
+  res[0].dims[2]=1;
+  res[1]=res[0];
+  res[1].name="scalarphase";
+  
+#pragma omp parallel for
+  for(size_t solutionIndex = 0; solutionIndex<_nAntennas*_nDirections; ++solutionIndex)
+  {
+    size_t thread =
+#ifdef AOPROJECT
+        omp_get_thread_num();
+#else
+        LOFAR::OpenMP::threadNum();
+#endif
+
+    for(size_t ch=0; ch!=_nChannelBlocks; ++ch) {
+      _phaseFitters[thread].PhaseData()[ch] = std::arg(solutions[ch][solutionIndex]);
+    }
+    
+    double alpha, beta=0.0;
+    if(_mode == TECOnlyMode) {
+      _phaseFitters[thread].FitDataToTEC1Model(alpha);
+    } else {
+      _phaseFitters[thread].FitDataToTEC2Model(alpha, beta);
+    }
+
+    res[0].vals[solutionIndex] = alpha / 8.44797245e9;
+    res[1].vals[solutionIndex] = beta;
+    
+    for(size_t ch=0; ch!=_nChannelBlocks; ++ch) {
+     solutions[ch][solutionIndex] = std::polar<double>(1.0, _phaseFitters[thread].PhaseData()[ch]);
+    }
+  }
+
+  return res;
+}
+
+std::vector<Constraint::Result> CoreConstraint::Apply(
+    std::vector<std::vector<dcomplex> >& solutions, double)
+{
+  for (uint ch=0; ch<solutions.size(); ++ch) {
+    std::vector<dcomplex> coreSolutions(_nDirections, 0.0);
+    // Calculate the sum of solutions over the core stations
+    for(std::set<size_t>::const_iterator antennaIter = _coreAntennas.begin(); antennaIter!=_coreAntennas.end(); ++antennaIter)
+    {
+      size_t startIndex = (*antennaIter)*_nDirections;
+      for(size_t direction = 0; direction != _nDirections; ++direction)
+        coreSolutions[direction] += solutions[ch][startIndex + direction];
+    }
+    
+    // Divide by nr of core stations to get the mean solution
+    for(std::vector<dcomplex>::iterator solutionIter = coreSolutions.begin(); solutionIter!=coreSolutions.end(); ++solutionIter)
+      (*solutionIter) /= _coreAntennas.size();
+    
+    // Assign all core stations to the mean solution
+    for(std::set<size_t>::const_iterator antennaIter = _coreAntennas.begin(); antennaIter!=_coreAntennas.end(); ++antennaIter)
+    {
+      size_t startIndex = (*antennaIter)*_nDirections;
+      for(size_t direction = 0; direction != _nDirections; ++direction)
+        solutions[ch][startIndex + direction] = coreSolutions[direction];
+    }
+  }
+  return std::vector<Constraint::Result>();
+}
diff --git a/CEP/DP3/DPPP_DDECal/src/DDECal.cc b/CEP/DP3/DPPP_DDECal/src/DDECal.cc
new file mode 100644
index 00000000000..bb094e25a01
--- /dev/null
+++ b/CEP/DP3/DPPP_DDECal/src/DDECal.cc
@@ -0,0 +1,557 @@
+//# DDECal.cc: DPPP step class to do a direction dependent gain calibration
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id: DDECal.cc 21598 2012-07-16 08:07:34Z diepen $
+//#
+//# @author Tammo Jan Dijkema
+
+#include <lofar_config.h>
+#include <DPPP_DDECal/DDECal.h>
+#include <DPPP/Simulate.h>
+#include <DPPP/ApplyCal.h>
+#include <DPPP/DPBuffer.h>
+#include <DPPP/DPInfo.h>
+#include <DPPP/SourceDBUtil.h>
+#include <DPPP/MSReader.h>
+#include <DPPP/DPLogger.h>
+#include <DPPP_DDECal/ScreenConstraint.h>
+#include <ParmDB/ParmDB.h>
+#include <ParmDB/ParmValue.h>
+#include <ParmDB/SourceDB.h>
+#include <Common/ParameterSet.h>
+#include <Common/StringUtil.h>
+#include <Common/LofarLogger.h>
+#include <Common/OpenMP.h>
+
+#include <fstream>
+#include <ctime>
+#include <utility>
+
+#include <casa/Arrays/ArrayMath.h>
+#include <casa/Arrays/MatrixMath.h>
+#include <measures/Measures/MEpoch.h>
+#include <measures/Measures/MeasConvert.h>
+#include <measures/Measures/MCDirection.h>
+#include <casa/Quanta/Quantum.h>
+#include <casa/OS/File.h>
+
+#include <vector>
+#include <algorithm>
+
+#include <limits>
+#include <iostream>
+#include <iomanip>
+
+using namespace casa;
+using namespace LOFAR::BBS;
+
+namespace LOFAR {
+  namespace DPPP {
+
+    DDECal::DDECal (DPInput* input,
+                      const ParameterSet& parset,
+                      const string& prefix)
+      : itsInput         (input),
+        itsName          (prefix),
+        itsAvgTime       (0),
+        itsSols          (),
+        itsH5ParmName    (parset.getString (prefix + "h5parm",
+                                            parset.getString("msin")+
+                                              "/instrument.h5")),
+        itsH5Parm        (itsH5ParmName),
+        itsTimeStep      (0),
+        itsSolInt        (parset.getInt (prefix + "solint", 1)),
+        itsStepInSolInt  (0),
+        itsNChan         (parset.getInt (prefix + "nchan", 0)),
+        itsNFreqCells    (0),
+        itsCoreConstraint(parset.getDouble (prefix + "coreconstraint", 0.0)),
+        itsMultiDirSolver(parset.getInt (prefix + "maxiter", 50),
+                          parset.getDouble (prefix + "tolerance", 1.e-5),
+                          parset.getDouble (prefix + "stepsize", 0.2))
+    {
+      vector<string> strDirections = parset.getStringVector (prefix + "directions");
+      itsDirections.resize(strDirections.size());
+      for (uint i=0; i<strDirections.size(); ++i) {
+        ParameterValue dirStr(strDirections[i]);
+        itsDirections[i] = dirStr.getStringVector();
+      }
+
+      itsMode = toLower(parset.getString(prefix + "mode", "complexgain"));
+      if(itsCoreConstraint != 0.0) {
+        itsConstraints.push_back(casa::CountedPtr<Constraint>(
+          new CoreConstraint()));
+      }
+      if (itsMode == "phaseonly") {
+        itsConstraints.push_back(casa::CountedPtr<Constraint>(
+                  new PhaseConstraint()));
+      } else if (itsMode == "tec") {
+        itsConstraints.push_back(casa::CountedPtr<Constraint>(
+                  new TECConstraint(TECConstraint::TECOnlyMode)));
+      } else if (itsMode == "tecandphase"){
+        itsConstraints.push_back(casa::CountedPtr<Constraint>(
+                  new TECConstraint(TECConstraint::TECAndCommonScalarMode)));
+      } else if (itsMode == "complexgain") {
+        // no constraints
+      } else if (itsMode == "tecscreen") {
+        itsConstraints.push_back(casa::CountedPtr<Constraint>(
+                  new ScreenConstraint()));
+      } else {
+        THROW (Exception, "Unexpected mode: " << itsMode);
+      }
+
+      itsDataPtrs.resize(itsSolInt);
+      itsModelDataPtrs.resize(itsSolInt);
+      for (uint t=0; t<itsSolInt; ++t) {
+        itsModelDataPtrs[t].resize(itsDirections.size());
+      }
+      for  (uint i=0;i<itsConstraints.size();i++) {
+        itsMultiDirSolver.add_constraint(itsConstraints[i].get());
+      }
+
+      const size_t nDir = itsDirections.size();
+
+      itsBufs.resize(itsSolInt);
+
+      itsPredictSteps.resize(nDir);
+
+      itsResultSteps.resize(nDir);
+      for (size_t dir=0; dir<nDir; ++dir) {
+        itsResultSteps[dir] = MultiResultStep::ShPtr(new MultiResultStep(itsSolInt));
+        itsPredictSteps[dir]=Predict(input, parset, prefix, itsDirections[dir]);
+        itsPredictSteps[dir].setNextStep(itsResultSteps[dir]);
+      }
+    }
+
+    DDECal::~DDECal()
+    {}
+
+    DPStep::ShPtr DDECal::makeStep (DPInput* input,
+                                    const ParameterSet& parset,
+                                    const std::string& prefix)
+    {
+      return DPStep::ShPtr(new DDECal(input, parset, prefix));
+    }
+
+    void DDECal::updateInfo (const DPInfo& infoIn)
+    {
+      info() = infoIn;
+      info().setNeedVisData();
+
+      const size_t nDir=itsDirections.size();
+
+      for (size_t dir=0; dir<nDir; ++dir) {
+        itsPredictSteps[dir].updateInfo(infoIn);
+      }
+
+      if (itsSolInt==0) {
+        itsSolInt=info().ntime();
+      }
+
+      if (itsNChan==0) {
+        itsNChan = info().nchan();
+      }
+      if (itsNChan>info().nchan()) {
+        itsNChan=info().nchan();
+      }
+      itsNFreqCells = info().nchan() / itsNChan;
+      if (itsNChan*itsNFreqCells<info().nchan()) { // If last freq cell is smaller
+        itsNFreqCells++;
+      }
+
+      // Convert from casa::Vector to std::vector
+      vector<int> ant1(info().getAnt1().size());
+      vector<int> ant2(info().getAnt2().size());
+      for (uint i=0; i<ant1.size(); ++i) {
+        ant1[i]=info().getAnt1()[i];
+        ant2[i]=info().getAnt2()[i];
+      }
+
+      // Fill antenna info in H5Parm, need to convert from casa types to std types
+      std::vector<std::string> antennaNames(info().antennaNames().size());
+      std::vector<std::vector<double> > antennaPos(info().antennaPos().size());
+      for (uint i=0; i<info().antennaNames().size(); ++i) {
+        antennaNames[i]=info().antennaNames()[i];
+        casa::Quantum<casa::Vector<double> > pos = info().antennaPos()[i].get("m");
+        antennaPos[i].resize(3);
+        antennaPos[i][0] = pos.getValue()[0];
+        antennaPos[i][1] = pos.getValue()[1];
+        antennaPos[i][2] = pos.getValue()[2];
+      }
+
+      itsH5Parm.addAntennas(antennaNames, antennaPos);
+
+      std::vector<std::string> sourceNames(itsDirections.size());
+      std::vector<std::pair<double, double> > sourcePositions(itsDirections.size());
+      for (uint i=0; i<itsDirections.size(); ++i) {
+        sourceNames[i]=itsDirections[i][0]; // This only gives the name of the first patch
+        sourcePositions[i]=itsPredictSteps[i].getFirstDirection();
+      }
+      itsH5Parm.addSources(sourceNames, sourcePositions);
+
+      uint nSolTimes = (info().ntime()+itsSolInt-1)/itsSolInt;
+      itsSols.resize(nSolTimes);
+      itsConstraintSols.resize(nSolTimes);
+
+      vector<double> chanFreqs(info().nchan());  //nChannelBlocks
+      for (uint i=0;i<info().chanFreqs().size();++i) {
+        chanFreqs[i]=info().chanFreqs()[i];
+      }
+
+      for (uint i=0; i<itsConstraints.size();++i) {
+        itsConstraints[i]->init(
+            info().antennaNames().size(),
+            itsDirections.size(),
+            info().nchan(), //nChannelBlocks
+            &(chanFreqs[0])
+        );
+        
+        CoreConstraint* coreConstraint = dynamic_cast<CoreConstraint*>(itsConstraints[i].get());
+        if(coreConstraint != 0)
+        {
+          double
+            refX = antennaPos[i][0],
+            refY = antennaPos[i][1],
+            refZ = antennaPos[i][2];
+          std::set<size_t> coreAntennaIndices;
+          const double coreDistSq = itsCoreConstraint*itsCoreConstraint;
+          for(size_t ant=0; ant!=antennaPos.size(); ++ant)
+          {
+            double
+              dx = refX - antennaPos[ant][0],
+              dy = refY - antennaPos[ant][1],
+              dz = refZ - antennaPos[ant][2],
+              distSq = dx*dx + dy*dy + dz*dz;
+            if(distSq <= coreDistSq)
+              coreAntennaIndices.insert(ant);
+          }
+          coreConstraint->setCoreAntennas(coreAntennaIndices);
+        }
+        
+        ScreenConstraint* screenConstraint = dynamic_cast<ScreenConstraint*>(itsConstraints[i].get());
+        if(screenConstraint != 0)
+        {
+          screenConstraint->setAntennaPositions(antennaPos);
+          screenConstraint->setDirections(sourcePositions);
+          screenConstraint->initPiercePoints();
+          //itsConstraints.setHeight(...);
+        }
+      }
+
+      uint nSt = info().antennaNames().size();
+      itsMultiDirSolver.init(nSt, nDir, info().nchan(), ant1, ant2);
+      for (uint i=0; i<nSolTimes; ++i) {
+        itsSols[i].resize(info().nchan());
+      }
+    }
+
+    void DDECal::show (std::ostream& os) const
+    {
+      os << "DDECal " << itsName << endl;
+      os << "  H5Parm:              " << itsH5ParmName <<endl;
+      os << "  solint:              " << itsSolInt <<endl;
+      os << "  nchan:               " << itsNChan <<endl;
+      os << "  directions:          " << itsDirections << endl;
+      os << "  mode (constraints):  " << itsMode << endl;
+      os << "  coreconstraint:      " << itsCoreConstraint << endl;
+      for (uint i=0; i<itsPredictSteps.size(); ++i) {
+        itsPredictSteps[i].show(os);
+      }
+    }
+
+    void DDECal::showTimings (std::ostream& os, double duration) const
+    {
+      double totaltime=itsTimer.getElapsed();
+      os << "  ";
+      FlagCounter::showPerc1 (os, itsTimer.getElapsed(), duration);
+      os << " DDECal " << itsName << endl;
+
+      os << "          ";
+      FlagCounter::showPerc1 (os, itsTimerPredict.getElapsed(), totaltime);
+      os << " of it spent in predict" << endl;
+
+      os << "          ";
+      FlagCounter::showPerc1 (os, itsTimerSolve.getElapsed(), totaltime);
+      os << " of it spent in estimating gains and computing residuals" << endl;
+
+      os << "          ";
+      FlagCounter::showPerc1 (os, itsTimerWrite.getElapsed(), totaltime);
+      os << " of it spent in writing gain solutions to disk" << endl;
+    }
+
+    bool DDECal::process (const DPBuffer& bufin)
+    {
+      itsTimer.start();
+
+      itsBufs[itsStepInSolInt].copy(bufin);
+      itsDataPtrs[itsStepInSolInt] = itsBufs[itsStepInSolInt].getData().data();
+
+      itsInput->fetchUVW(bufin, itsBufs[itsStepInSolInt], itsTimer);
+      itsInput->fetchWeights(bufin, itsBufs[itsStepInSolInt], itsTimer);
+      itsInput->fetchFullResFlags(bufin, itsBufs[itsStepInSolInt], itsTimer);
+
+      itsTimerPredict.start();
+
+#pragma omp parallel for
+      for (size_t dir=0; dir<itsPredictSteps.size(); ++dir) {
+        itsPredictSteps[dir].process(itsBufs[itsStepInSolInt]);
+        itsModelDataPtrs[itsStepInSolInt][dir] =
+                 itsResultSteps[dir]->get()[itsStepInSolInt].getData().data();
+      }
+
+      // Handle weights and flags
+      const size_t nBl = info().nbaselines();
+      const size_t nCh = info().nchan();
+      const size_t nCr = 4;
+      
+      for (size_t ch=0; ch<nCh; ++ch) {
+        for (size_t bl=0; bl<nBl; ++bl) {
+          for (size_t cr=0; cr<nCr; ++cr) {
+            if (itsBufs[itsStepInSolInt].getFlags().data()[bl*nCr*nCh+ch*nCr+cr]) {
+              // Flagged points: set data and model to 0
+              itsDataPtrs[itsStepInSolInt][bl*nCr*nCh+ch*nCr+cr] = 0;
+              for (size_t dir=0; dir<itsPredictSteps.size(); ++dir) {
+                itsModelDataPtrs[itsStepInSolInt][dir][bl*nCr*nCh+ch*nCr+cr] = 0;
+              }
+            } else {
+              // Premultiply non-flagged data with sqrt(weight)
+              double weight = sqrt(itsBufs[itsStepInSolInt].getWeights().data()[bl*nCr*nCh+ch*nCr+cr]);
+              itsDataPtrs[itsStepInSolInt][bl*nCr*nCh+ch*nCr+cr] *= weight;
+              for (size_t dir=0; dir<itsPredictSteps.size(); ++dir) {
+                itsModelDataPtrs[itsStepInSolInt][dir][bl*nCr*nCh+ch*nCr+cr] *= weight;
+              }
+            }
+          }
+        }
+      }
+  
+      itsTimerPredict.stop();
+
+      itsAvgTime += itsAvgTime + bufin.getTime();
+
+      if (itsStepInSolInt==itsSolInt-1) {
+        itsTimerSolve.start();
+        MultiDirSolver::SolveResult solveResult = 
+                  itsMultiDirSolver.process(itsDataPtrs, itsModelDataPtrs,
+                  itsSols[itsTimeStep/itsSolInt],
+                  itsAvgTime / itsSolInt);
+        itsTimerSolve.stop();
+
+        // Store constraint solutions
+        if (itsMode!="complexgain" && itsMode!="phaseonly") {
+          itsConstraintSols[itsTimeStep/itsSolInt]=solveResult._results;
+        }
+
+        itsStepInSolInt=0;
+        itsAvgTime=0;
+        for (size_t dir=0; dir<itsResultSteps.size(); ++dir) {
+          itsResultSteps[dir]->clear();
+        }
+      } else {
+        itsStepInSolInt++;
+      }
+
+      itsTimeStep++;
+      itsTimer.stop();
+
+      getNextStep()->process(bufin);
+
+      return false;
+    }
+
+    void DDECal::writeSolutions() {
+      itsTimer.start();
+      itsTimerWrite.start();
+
+      uint nDir = itsDirections.size();
+
+      vector<double> weights(info().nchan()*info().nantenna()*info().ntime()*itsDirections.size(),1.);
+
+      if (itsConstraintSols[0].empty()) {
+        // Record the actual iterands of the solver
+        vector<DComplex> sols(info().nchan()*info().nantenna()*info().ntime()*nDir);
+        size_t i=0;
+
+        // Put solutions in a contiguous piece of memory
+        for (uint dir=0; dir<nDir; ++dir) {
+          for (uint ant=0; ant<info().nantenna(); ++ant) {
+            for (uint chan=0; chan<info().nchan(); ++chan) {
+              for (uint time=0; time<itsSols.size(); ++time) {
+                ASSERT(!itsSols[time].empty());
+                sols[i] = itsSols[time][chan][ant*nDir+dir];
+                ++i;
+              }
+            }
+          }
+        }
+        string axesnames="dir,ant,freq,time";
+        vector<hsize_t> dims(4);
+        dims[0]=nDir;
+        dims[1]=info().nantenna();
+        dims[2]=info().nchan();
+        dims[3]=itsSols.size();
+
+        uint numsols=(itsMode=="complexgain"?2:1);
+        for (uint solnum=0; solnum<numsols; ++solnum) {
+          string solTabName;
+          if (itsMode=="phaseonly") {
+            solTabName = "phaseonly000";
+            itsH5Parm.addSolution(solTabName, "scalarphase", axesnames,
+                                  dims, sols, weights, false);
+          } else if (itsMode=="complexgain") {
+            if (solnum==0) {
+              solTabName = "scalarphase000";
+              itsH5Parm.addSolution(solTabName, "scalarphase", axesnames,
+                                    dims, sols, weights, false);
+            } else {
+              solTabName = "scalaramplitude000";
+              itsH5Parm.addSolution(solTabName, "scalaramplitude", axesnames,
+                                    dims, sols, weights, true);
+            }
+          } else {
+            THROW(Exception, "Constraint should have produced output");
+          }
+          // Tell H5Parm that all antennas and directions were used 
+          // TODO: do this more cleanly
+          std::vector<std::string> antennaNames(info().antennaNames().size());
+          for (uint i=0; i<info().antennaNames().size(); ++i) {
+            antennaNames[i]=info().antennaNames()[i];
+          }
+          itsH5Parm.setSolAntennas(solTabName, antennaNames);
+    
+          std::vector<std::string> sourceNames(itsDirections.size());
+          for (uint i=0; i<itsDirections.size(); ++i) {
+            sourceNames[i]=itsDirections[i][0]; // This only gives the name of the first patch
+          }
+          itsH5Parm.setSolSources(solTabName, sourceNames);
+   
+          // Set channel to frequency of middle channel 
+          vector<double> chanFreqs(info().nchan());
+          for (uint chan=0; chan<info().nchan(); ++chan) {
+            chanFreqs[chan] = info().chanFreqs()[chan];
+          }
+          itsH5Parm.setFreqs(solTabName, chanFreqs);
+    
+          uint nSolTimes = (info().ntime()+itsSolInt-1)/itsSolInt;
+          vector<double> solTimes(nSolTimes);
+          double starttime=info().startTime();
+          for (uint t=0; t<nSolTimes; ++t) {
+            solTimes[t] = starttime+t*info().timeInterval()*itsSolInt+0.5*info().timeInterval();
+          }
+          // End TODO
+          itsH5Parm.setTimes(solTabName, solTimes);
+        } // solnums loop
+      } else {
+        // Record the Constraint::Result in the H5Parm
+
+        uint nConstraints = itsConstraintSols[0].size();
+
+        for (uint constraintNum=0; constraintNum<nConstraints; ++constraintNum) {
+          // Number of solution names, e.g. 2 for "TEC" and "ScalarPhase"
+          uint nSolNames = itsConstraintSols[0][constraintNum].size();
+          for (uint solNameNum=0; solNameNum<nSolNames; ++solNameNum) {
+            // Get the result of the constraint solution at first time to get metadata
+            Constraint::Result firstResult = itsConstraintSols[0][constraintNum][solNameNum];
+
+            vector<hsize_t> dims(firstResult.dims.size()+1); // Add time dimension
+            dims[dims.size()-1]=itsConstraintSols.size();
+            size_t numSols=dims[dims.size()-1];
+            for (uint i=0; i<dims.size()-1; ++i) {
+              dims[i] = firstResult.dims[i]; 
+              numSols *= dims[i];
+            }
+
+            string axesnames = firstResult.axes + ",time";
+
+            // Put solutions in a contiguous piece of memory
+            vector<double> sols(numSols);
+            size_t posInFlatSol=0;
+            for (uint i=0; i<firstResult.vals.size(); ++i) {
+              for (uint time=0; time<itsSols.size(); ++time) {
+                sols[posInFlatSol++] = 
+                  itsConstraintSols[time][constraintNum][solNameNum].vals[i];
+              }
+            }
+
+            string solTabName = firstResult.name+"000";
+            itsH5Parm.addSolution(solTabName, firstResult.name, 
+                                  axesnames, dims, sols, weights);
+
+            // Tell H5Parm that all antennas and directions were used 
+            // TODO: do this more cleanly
+            std::vector<std::string> antennaNames(info().antennaNames().size());
+            for (uint i=0; i<info().antennaNames().size(); ++i) {
+              antennaNames[i]=info().antennaNames()[i];
+            }
+            itsH5Parm.setSolAntennas(solTabName, antennaNames);
+      
+            std::vector<std::string> sourceNames(itsDirections.size());
+            for (uint i=0; i<itsDirections.size(); ++i) {
+              sourceNames[i]=itsDirections[i][0]; // This only gives the name of the first patch
+            }
+            itsH5Parm.setSolSources(solTabName, sourceNames);
+     
+            // Set channel to frequency of middle channel 
+            vector<double> oneFreq(1);
+            oneFreq[0] = info().chanFreqs()[info().nchan()/2];
+            itsH5Parm.setFreqs(solTabName, oneFreq);
+      
+            uint nSolTimes = (info().ntime()+itsSolInt-1)/itsSolInt;
+            vector<double> solTimes(nSolTimes);
+            double starttime=info().startTime();
+            for (uint t=0; t<nSolTimes; ++t) {
+              solTimes[t] = starttime+t*info().timeInterval()*itsSolInt+0.5*info().timeInterval();
+            }
+            itsH5Parm.setTimes(solTabName, solTimes);
+            // End TODO 
+          }
+        }
+      }
+
+
+      itsTimerWrite.stop();
+      itsTimer.stop();
+    }
+
+    void DDECal::finish()
+    {
+      itsTimer.start();
+
+      if (itsStepInSolInt!=0) {
+        //shrink itsDataPtrs, itsModelDataPtrs
+        std::vector<casa::Complex*>(itsDataPtrs.begin(),
+            itsDataPtrs.begin()+itsStepInSolInt).swap(itsDataPtrs);
+        std::vector<std::vector<casa::Complex*> >(itsModelDataPtrs.begin(),
+                    itsModelDataPtrs.begin()+itsStepInSolInt).swap(itsModelDataPtrs);
+        itsTimerSolve.start();
+        itsMultiDirSolver.process(itsDataPtrs, itsModelDataPtrs,
+                                  itsSols[itsTimeStep/itsSolInt],
+                                  itsAvgTime/itsStepInSolInt);
+        itsTimerSolve.stop();
+      }
+
+      writeSolutions();
+
+      itsTimer.stop();
+
+      // Let the next steps finish.
+      getNextStep()->finish();
+    }
+
+  } //# end namespace
+}
diff --git a/CEP/DP3/DPPP_DDECal/src/KLFitter.cc b/CEP/DP3/DPPP_DDECal/src/KLFitter.cc
new file mode 100644
index 00000000000..2a87522e16c
--- /dev/null
+++ b/CEP/DP3/DPPP_DDECal/src/KLFitter.cc
@@ -0,0 +1,43 @@
+#include "DPPP_DDECal/KLFitter.h"
+using namespace arma;
+
+KLFitter::KLFitter(double r0,double beta,int order):
+  itsOrder(order),
+  itsR0(r0),
+  itsBeta(beta)
+{
+  
+}
+
+
+
+void KLFitter::calculateCorrMatrix(const vector<PiercePoint> pp){
+  itsPiercePoints.set_size(pp.size(),3);
+  _phases.set_size(pp.size());
+  _weights=eye<mat>(pp.size(),pp.size());
+  Mat<double> Distance=zeros<mat>(pp.size(),pp.size());
+  for(size_t i=0; i<pp.size();i++){
+    Mat<double> A(pp[i].getValue().memptr(),1,3);
+    itsPiercePoints.row(i)=A;
+  }
+  for(size_t n=0;n<pp.size();n++)
+    for(size_t m=0;m<pp.size();m++)
+      for(size_t i=0;i<3;i++)
+	Distance(n,m)+=pow(itsPiercePoints.col(i)[n]-itsPiercePoints.col(i)[m],2);
+  itsCorrMatrix=-pow((Distance / ( itsR0*itsR0 ) ),( itsBeta / 2.0 ))/2.0;
+  itsinvC=pinv(itsCorrMatrix);
+  mat V,U;
+  Col<double> S;
+  svd(U,S,V,itsCorrMatrix);
+  itsU=U(span::all,span(0,itsOrder));
+  itsinvU=inv(itsU.t()*(_weights*itsU)); 
+}
+
+
+void KLFitter::doFit(){
+  Mat<double> A=itsU.t()*(_weights*_phases);
+  itsPar=itsinvU* A; //TODO add weight info
+  itsTECFitWhite=(itsinvC*(itsU*itsPar));
+
+  _phases=itsCorrMatrix*itsTECFitWhite;
+}
diff --git a/CEP/DP3/DPPP_DDECal/src/PiercePoint.cc b/CEP/DP3/DPPP_DDECal/src/PiercePoint.cc
new file mode 100644
index 00000000000..5f9c399c516
--- /dev/null
+++ b/CEP/DP3/DPPP_DDECal/src/PiercePoint.cc
@@ -0,0 +1,50 @@
+#include <DPPP_DDECal/PiercePoint.h>
+using namespace arma;
+
+const double PiercePoint::IONOheight = 300000.; //default height in meter
+const double PiercePoint::EarthRadius = 6371000.; //default Earth radius in meter
+
+PiercePoint::PiercePoint():
+  itsValue(3)
+{
+  casa::MPosition ant;//ITRF
+  casa::MDirection source;//J2000 pole
+  init(ant,source,PiercePoint::IONOheight);
+}
+
+PiercePoint::PiercePoint(const casa::MPosition &ant,const casa::MDirection &source,const double height):
+  itsValue(3)
+{
+  init(ant,source,height);
+};
+
+PiercePoint::PiercePoint(const casa::MPosition &ant,const casa::MDirection &source):
+   itsValue(3)
+  { 
+    init(ant,source,PiercePoint::IONOheight);
+};
+
+void  PiercePoint::init(const casa::MPosition &ant,const casa::MDirection &source,const double height){
+  
+  itsPosition=casa::MPosition::Convert(ant,casa::MPosition::ITRF)();
+  itsDirection=source;
+  itsIonoHeight=height;
+  const casa::MVPosition &mPosition = itsPosition.getValue();
+  itsC = mPosition(0)*mPosition(0)+mPosition(1)*mPosition(1)+mPosition(2)*mPosition(2)-
+    (itsIonoHeight+PiercePoint::EarthRadius)*(itsIonoHeight+PiercePoint::EarthRadius);
+  
+}
+
+void  PiercePoint::evaluate(casa::MEpoch time){
+  //Convert direction to ITRF vector
+  casa::MeasFrame myframe(itsPosition,time);
+  casa::MDirection::Ref myref(casa::MDirection::ITRF,myframe);
+  const casa::MDirection dir = casa::MDirection::Convert(itsDirection,myref)();
+  const casa::MVDirection &mDir = dir.getValue();
+  const casa::MVPosition &mPos = itsPosition.getValue();
+  double A = mDir(0)*mDir(0)+mDir(1)*mDir(1)+mDir(2)*mDir(2);
+  double B = mDir(0)*mPos(0) + mDir(1)*mPos(1) +mDir(2)*mPos(2);
+  double alpha = (-B + sqrt(B*B - A*itsC))/A;
+  for(uword i=0;i<3;i++)
+    itsValue(i) = mPos(i) + alpha*mDir(i);
+};
diff --git a/CEP/DP3/DPPP_DDECal/src/Register.cc b/CEP/DP3/DPPP_DDECal/src/Register.cc
new file mode 100644
index 00000000000..856e6fdf318
--- /dev/null
+++ b/CEP/DP3/DPPP_DDECal/src/Register.cc
@@ -0,0 +1,36 @@
+//# Register.cc: Register steps in DPPP
+//# Copyright (C) 2015
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id: AOFlaggerStep.cc 31423 2015-04-03 14:06:21Z dijkema $
+//#
+//# @author Ger van Diepen
+
+#include <lofar_config.h>
+#include <DPPP_DDECal/Register.h>
+#include <DPPP_DDECal/DDECal.h>
+#include <DPPP/DPRun.h>
+
+// Define the function to make the DDECal 'constructor' known.
+// Its suffix must be the (lowercase) name of the package (library).
+// Also make the SlidingFlagger known.
+void register_ddecal()
+{
+  LOFAR::DPPP::DPRun::registerStepCtor ("ddecal",
+                                        LOFAR::DPPP::DDECal::makeStep);
+}
diff --git a/CEP/DP3/DPPP_DDECal/src/ScreenConstraint.cc b/CEP/DP3/DPPP_DDECal/src/ScreenConstraint.cc
new file mode 100644
index 00000000000..0f00d3e1de6
--- /dev/null
+++ b/CEP/DP3/DPPP_DDECal/src/ScreenConstraint.cc
@@ -0,0 +1,158 @@
+#include <DPPP_DDECal/ScreenConstraint.h>
+#include <Common/OpenMP.h>
+
+
+ScreenConstraint::ScreenConstraint() :
+  _nAntennas(0),
+  _nDirections(0),
+  _nChannelBlocks(0),
+  itsCurrentTime(0)
+{
+}
+
+void ScreenConstraint::init(size_t nAntennas, size_t nDirections, size_t nChannelBlocks, const double* frequencies) {
+  _nAntennas = nAntennas;
+  _nDirections = nDirections;
+  _nChannelBlocks = nChannelBlocks;
+  itsFrequencies.resize(_nChannelBlocks);
+  std::memcpy( itsFrequencies.data(),frequencies, sizeof(double) * _nChannelBlocks);
+  itsAntennaPos.resize(_nAntennas);
+  itsSourcePos.resize(_nDirections);
+  itsPiercePoints.resize(_nAntennas);
+  for(uint i=0;i<itsPiercePoints.size();i++)
+    itsPiercePoints[i].resize(_nDirections);
+  _screenFitters.resize(_nAntennas);
+
+  /*  for(size_t i=0; i!=_screenFitters.size(); ++i)
+  {
+    _screenFitters[i].SetChannelCount(_nChannelBlocks);
+    std::memcpy(_screenFitters[i].FrequencyData(), frequencies, sizeof(double) * _nChannelBlocks);
+    _screenFitters[i].SetPPCount(_nDirections);
+
+    }
+  */
+}
+
+void ScreenConstraint::setAntennaPositions(const std::vector<std::vector<double> > antenna_pos) {
+  for(uint i=0;i<antenna_pos.size();i++){
+    itsAntennaPos[i].resize(3);
+    for(int j=0;j<3;j++)
+      itsAntennaPos[i][j]=antenna_pos[i][j];
+  }
+}
+
+void ScreenConstraint::setDirections(const std::vector<std::pair<double, double> > source_pos) {
+  for(uint i=0;i<source_pos.size();i++){
+    itsSourcePos[i].resize(2);
+    itsSourcePos[i][0]=source_pos[i].first;
+    itsSourcePos[i][1]=source_pos[i].second;
+  }
+
+}
+
+void ScreenConstraint::initPiercePoints(){
+  for(uint ipos=0;ipos<itsAntennaPos.size();ipos++){
+    casa::MPosition ant(casa::MVPosition(itsAntennaPos[ipos][0],itsAntennaPos[ipos][1],itsAntennaPos[ipos][2]),
+			casa::MPosition::ITRF);
+    for(uint isrc=0;isrc<itsSourcePos.size();isrc++){
+      casa::MDirection src(casa::MVDirection(itsSourcePos[isrc][0],itsSourcePos[isrc][1]),
+			   casa::MDirection::J2000);
+	  
+      itsPiercePoints[ipos][isrc]=PiercePoint(ant,src);
+    }
+  }
+}
+
+void ScreenConstraint::setTime(double time){
+  if (itsCurrentTime!=time){
+      
+      itsCurrentTime=time;
+      CalculatePiercepoints();
+#pragma omp parallel for
+      for(uint ipos=0;ipos<itsAntennaPos.size();ipos++){
+	_screenFitters[ipos].calculateCorrMatrix(itsPiercePoints[ipos]);
+      }
+
+    }
+}
+
+void ScreenConstraint::CalculatePiercepoints(){
+   casa::MEpoch time(casa::MVEpoch(itsCurrentTime/(24.*3600.))); //convert to MJD
+  for (uint i=0;i<itsPiercePoints.size();i++)
+    for (uint j=0;j<itsPiercePoints[i].size();j++)
+      itsPiercePoints[i][j].evaluate(time);
+}
+
+
+std::vector<Constraint::Result> ScreenConstraint::Apply(std::vector<std::vector<MultiDirSolver::DComplex> >& solutions,double time) {
+  //check if we need to reinitialize piercepoints
+  setTime(time);
+
+  std::vector<Result> res(4);
+  size_t numberofPar=std::min(_screenFitters[0].getOrder(),_nDirections);
+  res[0].vals.resize(_nAntennas*numberofPar);
+  res[0].axes="antenna,par";
+  res[0].dims.resize(2);
+  res[0].dims[0]=_nAntennas;
+  res[0].dims[1]=numberofPar;
+  res[0].name="screenpar";
+  res[1].vals.resize(_nAntennas*_nDirections*3);
+  res[1].axes="antenna,dir,xyz";
+  res[1].dims.resize(3);
+  res[1].dims[0]=_nAntennas;
+  res[1].dims[1]=_nDirections;
+  res[1].dims[2]=3;
+  res[1].name="piercepoints";
+  res[2].vals.resize(_nAntennas*_nDirections);
+  res[2].axes="antenna,dir";
+  res[2].dims.resize(2);
+  res[2].dims[0]=_nAntennas;
+  res[2].dims[1]=_nDirections;
+  res[2].name="TECfitwhite";
+  res[3].vals.resize(_nAntennas*_nDirections);
+  res[3].axes="antenna,dir";
+  res[3].dims.resize(2);
+  res[3].dims[0]=_nAntennas;
+  res[3].dims[1]=_nDirections;
+  res[3].name="phases";
+
+  //TODOEstimate Weights
+  
+
+#pragma omp parallel for
+  for(size_t antIndex = 0; antIndex<_nAntennas; ++antIndex)
+    {
+      for(size_t dirIndex = 0; dirIndex<_nDirections; ++dirIndex){
+	double avgTEC=0;
+	size_t solutionIndex=antIndex*_nDirections+dirIndex;
+	for(size_t ch=0;ch<_nChannelBlocks; ++ch){
+	  double refphase=std::arg(solutions[ch][dirIndex]);
+	  //TODO: more advance frequency averaging...
+	  avgTEC += std::arg(solutions[ch][solutionIndex]*std::polar<double>(1.0,-1*refphase))*itsFrequencies[ch];
+	}
+ 	res[3].vals[antIndex*_nDirections+dirIndex]= avgTEC/_nChannelBlocks;
+	_screenFitters[antIndex].PhaseData()[dirIndex] = avgTEC/_nChannelBlocks;
+      }
+      
+        _screenFitters[antIndex].doFit();
+    
+      for(size_t dirIndex = 0; dirIndex<_nDirections; ++dirIndex){
+	size_t solutionIndex=antIndex*_nDirections+dirIndex;
+	for(size_t ch=0;ch<_nChannelBlocks; ++ch)
+	  solutions[ch][solutionIndex] = std::polar<double>(1.0, _screenFitters[antIndex].PhaseData()[dirIndex]/itsFrequencies[ch]);
+	//res[3].vals[antIndex*_nDirections+dirIndex]= _screenFitters[antIndex].PhaseData()[dirIndex];
+	for (size_t i=0;i<3;i++)
+	  res[1].vals[antIndex*_nDirections*3+dirIndex*3+i]= _screenFitters[antIndex].PPData()[i*_nDirections+dirIndex]; 
+      }
+      for(size_t i=0;i<numberofPar;i++)
+	res[0].vals[antIndex*numberofPar+i]= _screenFitters[antIndex].ParData()[i];
+      for(size_t dirIndex = 0; dirIndex<_nDirections; ++dirIndex)
+	res[2].vals[antIndex*_nDirections+dirIndex]=
+	  _screenFitters[antIndex].TECFitWhiteData()[dirIndex];
+	
+      
+    }
+   
+  
+  return res;
+}
diff --git a/CEP/DP3/DPPP_DDECal/src/multidirsolver.cc b/CEP/DP3/DPPP_DDECal/src/multidirsolver.cc
new file mode 100644
index 00000000000..643cb4aadc0
--- /dev/null
+++ b/CEP/DP3/DPPP_DDECal/src/multidirsolver.cc
@@ -0,0 +1,209 @@
+#ifdef AOPROJECT
+#include "multidirsolver.h"
+#else
+#include <DPPP_DDECal/multidirsolver.h>
+#endif
+
+using namespace arma;
+
+MultiDirSolver::MultiDirSolver(size_t maxIterations, double accuracy, double stepSize) :
+  _nAntennas(0),
+  _nDirections(0),
+  _nChannels(0),
+  _nChannelBlocks(0),
+  _mode(CalibrateComplexGain),
+  _maxIterations(maxIterations),
+  _accuracy(accuracy),
+  _stepSize(stepSize)
+{
+}
+
+void MultiDirSolver::init(size_t nAntennas,
+                          size_t nDirections,
+                          size_t nChannels,
+                          const std::vector<int>& ant1,
+                          const std::vector<int>& ant2)
+{
+  _nAntennas = nAntennas;
+  _nDirections = nDirections;
+  _nChannels = nChannels;
+  _nChannelBlocks = nChannels;
+  _ant1 = ant1;
+  _ant2 = ant2;
+}
+
+MultiDirSolver::SolveResult MultiDirSolver::process(std::vector<Complex *>& data,
+  std::vector<std::vector<Complex *> >& modelData,
+  std::vector<std::vector<DComplex> >& solutions, double time) const
+{
+  const size_t nTimes = data.size();
+  SolveResult result;
+  
+  std::vector<std::vector<DComplex> > nextSolutions(_nChannelBlocks);
+  if (solutions.size() != _nChannelBlocks) {
+    cout << "Error: 'solutions' parameter does not have the right shape" << endl;
+    result.iterations = 0;
+    return result;
+  }
+
+  result._results.resize(_constraints.size());
+  
+  // Model matrix ant x [N x D] and visibility matrix ant x [N x 1],
+  // for each channelblock
+  // The following loop allocates all structures
+  std::vector<std::vector<cx_mat> > gTimesCs(_nChannelBlocks);
+  std::vector<std::vector<cx_vec> > vs(_nChannelBlocks);
+  for(size_t chBlock=0; chBlock!=_nChannelBlocks; ++chBlock)
+  {
+    solutions[chBlock].assign(_nDirections * _nAntennas, 1.0);
+    nextSolutions[chBlock].resize(_nDirections * _nAntennas);
+    const size_t
+      channelIndexStart = chBlock * _nChannels / _nChannelBlocks,
+      channelIndexEnd = (chBlock+1) * _nChannels / _nChannelBlocks,
+      curChannelBlockSize = channelIndexEnd - channelIndexStart;
+    gTimesCs[chBlock].resize(_nAntennas);
+    vs[chBlock].resize(_nAntennas);
+    
+    for(size_t ant=0; ant!=_nAntennas; ++ant)
+    {
+      // Model matrix [N x D] and visibility matrix x [N x 1]
+      // Also space for the auto correlation is reserved, but it will be set to 0.
+      gTimesCs[chBlock][ant] = cx_mat(_nAntennas * nTimes * curChannelBlockSize,
+                                      _nDirections, fill::zeros);
+      vs[chBlock][ant] = cx_vec(_nAntennas * nTimes * curChannelBlockSize, fill::zeros);
+    }
+  }
+  
+  // TODO the data and model data needs to be preweighted.
+  // Maybe we can get a non-const pointer from DPPP, that saves copying/allocating
+  
+  ///
+  /// Start iterating
+  ///
+  size_t iteration = 0;
+  double normSum = 0.0, sum = 0.0;
+  do {
+#pragma omp parallel for
+    for(size_t chBlock=0; chBlock<_nChannelBlocks; ++chBlock)
+    {
+      performSolveIteration(chBlock, gTimesCs[chBlock], vs[chBlock],
+                            solutions[chBlock], nextSolutions[chBlock],
+                            data, modelData);
+    }
+      
+    // Move the solutions towards nextSolutions
+    // (the moved solutions are stored in 'nextSolutions')
+    for(size_t chBlock=0; chBlock!=_nChannelBlocks; ++chBlock)
+    {
+      for(size_t i=0; i!=_nAntennas*_nDirections; ++i)
+      {
+        nextSolutions[chBlock][i] = solutions[chBlock][i]*(1.0-_stepSize) +
+          nextSolutions[chBlock][i] * _stepSize;
+      }
+    }
+    for(size_t i=0; i!=_constraints.size(); ++i)
+    {
+      result._results[i] = _constraints[i]->Apply(nextSolutions, time);
+    }
+    
+    //  Calculate the norm of the difference between the old and new solutions
+    for(size_t chBlock=0; chBlock!=_nChannelBlocks; ++chBlock)
+    {
+      for(size_t i=0; i!=_nAntennas*_nDirections; ++i)
+      {
+        double e = std::norm(nextSolutions[chBlock][i] - solutions[chBlock][i]);
+        normSum += e;
+        sum += std::abs(solutions[chBlock][i]);
+        
+        solutions[chBlock][i] = nextSolutions[chBlock][i];
+        
+        // For debug: output the solutions of the first antenna
+        if(i<_nDirections && false)
+        {
+          std::cout << " |s_" << i << "|=|" << solutions[chBlock][i] << "|="
+                    << std::abs(solutions[chBlock][i]);
+        }
+      }
+    }
+    normSum /= _nChannelBlocks*_nAntennas*_nDirections;
+    sum /= _nChannelBlocks*_nAntennas*_nDirections;
+    iteration++;
+    
+  } while(iteration < _maxIterations && normSum/sum > _accuracy);
+  
+  if(normSum/sum <= _accuracy)
+    result.iterations = iteration;
+  else
+    result.iterations = _maxIterations+1;
+  return result;
+}
+
+void MultiDirSolver::performSolveIteration(size_t channelBlockIndex,
+                       std::vector<arma::cx_mat>& gTimesCs,
+                       std::vector<arma::cx_vec>& vs,
+                       const std::vector<DComplex>& solutions,
+                       std::vector<DComplex>& nextSolutions,
+                       const std::vector<Complex *>& data,
+                       const std::vector<std::vector<Complex *> >& modelData) const
+{
+  const size_t
+    channelIndexStart = channelBlockIndex * _nChannels / _nChannelBlocks,
+    channelIndexEnd = (channelBlockIndex+1) * _nChannels / _nChannelBlocks,
+    curChannelBlockSize = channelIndexEnd - channelIndexStart,
+    nTimes = data.size();
+  
+  // The following loop fills the matrices for all antennas
+  for(size_t timeIndex=0; timeIndex!=nTimes; ++timeIndex)
+  {
+    std::vector<const Complex*> modelPtrs(_nDirections);
+    for(size_t baseline=0; baseline!=_ant1.size(); ++baseline)
+    {
+      size_t antenna1 = _ant1[baseline];
+      size_t antenna2 = _ant2[baseline];
+      if(antenna1 != antenna2)
+      {
+        cx_mat& gTimesC1 = gTimesCs[antenna1];
+        cx_vec& v1 = vs[antenna1];
+        cx_mat& gTimesC2 = gTimesCs[antenna2];
+        cx_vec& v2 = vs[antenna2];
+        for(size_t d=0; d!=_nDirections; ++d)
+          modelPtrs[d] = modelData[timeIndex][d] + (channelIndexStart + baseline * _nChannels) * 4;
+        const Complex* dataPtr = data[timeIndex] + (channelIndexStart + baseline * _nChannels) * 4;
+        for(size_t ch=channelIndexStart; ch!=channelIndexEnd; ++ch)
+        {
+          size_t dataIndex1 = ch-channelIndexStart + (timeIndex + antenna1 * nTimes) * curChannelBlockSize;
+          size_t dataIndex2 = ch-channelIndexStart + (timeIndex + antenna2 * nTimes) * curChannelBlockSize;
+          //std::cout << "timeindex" << timeIndex << ' ';
+          for(size_t d=0; d!=_nDirections; ++d)
+          {
+            std::complex<double> predicted = modelPtrs[d][0] + modelPtrs[d][3];
+            //std::cout << predicted << ' ';
+            
+            size_t solIndex1 = antenna1*_nDirections + d;
+            size_t solIndex2 = antenna2*_nDirections + d;
+            gTimesC1(dataIndex2, d) = solutions[solIndex2] * predicted;
+            gTimesC2(dataIndex1, d) = solutions[solIndex1] * predicted;
+            
+            modelPtrs[d] += 4; // Goto the next 2x2 matrix.
+          }
+          v1(dataIndex2) = dataPtr[0] + dataPtr[3]; // Solve using Stokes I
+          v2(dataIndex1) = v1(dataIndex2);
+          dataPtr += 4; // Goto the next 2x2 matrix.
+        }
+      }
+    }
+  }
+  
+  // The matrices have been filled; compute the linear solution
+  // for each antenna.
+  for(size_t ant=0; ant!=_nAntennas; ++ant)
+  {
+    //std::cout << ant << '\n';
+    cx_mat& gTimesC = gTimesCs[ant];
+    cx_vec& v = vs[ant];
+    // solve [g C] x  = v
+    cx_vec x = solve(gTimesC, v);
+    for(size_t d=0; d!=_nDirections; ++d)
+      nextSolutions[ant*_nDirections + d] = x(d);
+  }
+}
diff --git a/CEP/DP3/DPPP_DDECal/src/screenfitter.cc b/CEP/DP3/DPPP_DDECal/src/screenfitter.cc
new file mode 100644
index 00000000000..18563530647
--- /dev/null
+++ b/CEP/DP3/DPPP_DDECal/src/screenfitter.cc
@@ -0,0 +1,8 @@
+
+#include <DPPP/screenfitter.h>
+
+ScreenFitter::ScreenFitter():
+    _phases(),
+    _frequencies(),
+    _weights()
+{ }
diff --git a/CMake/LofarPackageList.cmake b/CMake/LofarPackageList.cmake
index a1fca4d0c42..1b171d44aad 100644
--- a/CMake/LofarPackageList.cmake
+++ b/CMake/LofarPackageList.cmake
@@ -36,6 +36,7 @@ if(NOT DEFINED LOFAR_PACKAGE_LIST_INCLUDED)
   set(TestDynDPPP_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/DP3/TestDynDPPP)
   set(PythonDPPP_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/DP3/PythonDPPP)
   set(DPPP_AOFlag_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/DP3/DPPP_AOFlag)
+  set(DPPP_DDECal_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/DP3/DPPP_DDECal)
   set(SPW_Combine_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/DP3/SPWCombine)
   set(AOFlagger_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/DP3/AOFlagger)
   set(LofarFT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/Imager/LofarFT)
-- 
GitLab