From c542f000b9c2e54b2861bd7fef3fcd53f514dc6c Mon Sep 17 00:00:00 2001
From: Roman Tolchenov <roman.tolchenov@stfc.ac.uk>
Date: Wed, 14 Mar 2012 15:50:21 +0000
Subject: [PATCH] Re #4158. Added MultiDomainFunction

---
 Code/Mantid/Framework/API/CMakeLists.txt      |   7 +
 .../API/inc/MantidAPI/CompositeFunction.h     |  21 +-
 .../API/inc/MantidAPI/FunctionValues.h        |   3 +-
 .../Framework/API/inc/MantidAPI/JointDomain.h |  64 +++++
 .../API/inc/MantidAPI/MultiDomainFunction.h   |  80 ++++++
 .../Framework/API/src/CompositeFunction.cpp   |   2 -
 .../Framework/API/src/FunctionValues.cpp      |  17 ++
 Code/Mantid/Framework/API/src/JointDomain.cpp |  35 +++
 .../Framework/API/src/MultiDomainFunction.cpp | 174 +++++++++++++
 .../API/test/MultiDomainFunctionTest.h        | 245 ++++++++++++++++++
 .../Framework/CurveFitting/CMakeLists.txt     |   1 +
 .../MantidCurveFitting/CostFuncLeastSquares.h |   9 +
 .../inc/MantidCurveFitting/GSLVector.h        |   6 +
 .../CurveFitting/src/CostFuncLeastSquares.cpp | 178 +++++++++----
 .../src/LevenbergMarquardtMDMinimizer.cpp     |   1 +
 .../test/MultiDomainFunctionTest.h            | 140 ++++++++++
 16 files changed, 919 insertions(+), 64 deletions(-)
 create mode 100644 Code/Mantid/Framework/API/inc/MantidAPI/JointDomain.h
 create mode 100644 Code/Mantid/Framework/API/inc/MantidAPI/MultiDomainFunction.h
 create mode 100644 Code/Mantid/Framework/API/src/JointDomain.cpp
 create mode 100644 Code/Mantid/Framework/API/src/MultiDomainFunction.cpp
 create mode 100644 Code/Mantid/Framework/API/test/MultiDomainFunctionTest.h
 create mode 100644 Code/Mantid/Framework/CurveFitting/test/MultiDomainFunctionTest.h

diff --git a/Code/Mantid/Framework/API/CMakeLists.txt b/Code/Mantid/Framework/API/CMakeLists.txt
index d600963e290..a664cac83c9 100644
--- a/Code/Mantid/Framework/API/CMakeLists.txt
+++ b/Code/Mantid/Framework/API/CMakeLists.txt
@@ -53,6 +53,7 @@ set ( SRC_FILES
 	src/ImplictFunctionFactory.cpp
 	src/InstrumentDataService.cpp
 	src/LiveListenerFactory.cpp
+	src/JointDomain.cpp
 	src/LoadAlgorithmFactory.cpp
 	src/LocatedDataRef.cpp
 	src/LocatedDataValue.cpp
@@ -63,6 +64,8 @@ set ( SRC_FILES
 	src/MemoryManager.cpp
 	src/MultipleExperimentInfos.cpp
 	src/MultipleFileProperty.cpp
+	src/MultiDomainFunction.cpp
+	src/NumericAxis.cpp
 	src/NullCoordTransform.cpp
 	src/NumericAxis.cpp
 	src/ParamFunction.cpp
@@ -170,6 +173,7 @@ set ( INC_FILES
 	inc/MantidAPI/InstrumentDataService.h
 	inc/MantidAPI/Jacobian.h
 	inc/MantidAPI/LiveListenerFactory.h
+	inc/MantidAPI/JointDomain.h
 	inc/MantidAPI/LoadAlgorithmFactory.h
 	inc/MantidAPI/LocatedDataRef.h
 	inc/MantidAPI/LocatedDataValue.h
@@ -180,6 +184,8 @@ set ( INC_FILES
 	inc/MantidAPI/MemoryManager.h
 	inc/MantidAPI/MultipleExperimentInfos.h
 	inc/MantidAPI/MultipleFileProperty.h
+	inc/MantidAPI/MultiDomainFunction.h
+	inc/MantidAPI/NumericAxis.h
 	inc/MantidAPI/NullCoordTransform.h
 	inc/MantidAPI/NumericAxis.h
 	inc/MantidAPI/ParamFunction.h
@@ -247,6 +253,7 @@ set ( TEST_FILES
 	test/MDGeometryTest.h
 	test/MatrixWorkspaceMDIteratorTest.h
 	test/MemoryManagerTest.h
+	test/MultiDomainFunctionTest.h
 	test/MultipleExperimentInfosTest.h
 	test/MultipleFilePropertyTest.h
 	test/NumericAxisTest.h
diff --git a/Code/Mantid/Framework/API/inc/MantidAPI/CompositeFunction.h b/Code/Mantid/Framework/API/inc/MantidAPI/CompositeFunction.h
index ab75819c402..d8b405259b5 100644
--- a/Code/Mantid/Framework/API/inc/MantidAPI/CompositeFunction.h
+++ b/Code/Mantid/Framework/API/inc/MantidAPI/CompositeFunction.h
@@ -197,15 +197,21 @@ typedef boost::shared_ptr<const CompositeFunction> CompositeFunction_const_sptr;
 class PartialJacobian: public Jacobian
 {
   Jacobian* m_J;  ///< pointer to the overall Jacobian
-  size_t m_iP0;      ///< offset in the overall Jacobian for a particular function
-  //size_t m_iaP0;      ///< offset in the active Jacobian for a particular function
+  size_t m_iY0;      ///< fitting data index offset in the overall Jacobian for a particular function
+  size_t m_iP0;      ///< parameter index offset in the overall Jacobian for a particular function
 public:
   /** Constructor
    * @param J :: A pointer to the overall Jacobian
    * @param iP0 :: The parameter index (declared) offset for a particular function
-   * @param iap0 :: The active parameter index (declared) offset for a particular function
    */
-  PartialJacobian(Jacobian* J,size_t iP0):m_J(J),m_iP0(iP0)//,m_iaP0(iap0)
+  PartialJacobian(Jacobian* J,size_t iP0):m_J(J),m_iY0(0),m_iP0(iP0)//,m_iaP0(iap0)
+  {}
+  /** Constructor
+   * @param J :: A pointer to the overall Jacobian
+   * @param iY0 :: The data index offset for a particular function
+   * @param iP0 :: The parameter index offset for a particular function
+   */
+  PartialJacobian(Jacobian* J,size_t iY0,size_t iP0):m_J(J),m_iY0(iY0),m_iP0(iP0)
   {}
   /**
    * Overridden Jacobian::set(...).
@@ -215,7 +221,7 @@ public:
    */
   void set(size_t iY, size_t iP, double value)
   {
-      m_J->set(iY,m_iP0 + iP,value);
+      m_J->set(m_iY0 + iY,m_iP0 + iP,value);
   }
   /**
    * Overridden Jacobian::get(...).
@@ -224,11 +230,11 @@ public:
    */
   double get(size_t iY, size_t iP)
   {
-      return m_J->get(iY,m_iP0 + iP);
+      return m_J->get(m_iY0 + iY,m_iP0 + iP);
   }
  /**  Add number to all iY (data) Jacobian elements for a given iP (parameter)
   *   @param value :: Value to add
-  *   @param iActiveP :: The index of an active parameter.
+  *   @param iP :: The index of an active parameter.
   */
   virtual void addNumberToColumn(const double& value, const size_t& iP) 
   {
@@ -236,6 +242,7 @@ public:
   }
 };
 
+
 } // namespace API
 } // namespace Mantid
 
diff --git a/Code/Mantid/Framework/API/inc/MantidAPI/FunctionValues.h b/Code/Mantid/Framework/API/inc/MantidAPI/FunctionValues.h
index 4e9373ec4da..07a1b40740e 100644
--- a/Code/Mantid/Framework/API/inc/MantidAPI/FunctionValues.h
+++ b/Code/Mantid/Framework/API/inc/MantidAPI/FunctionValues.h
@@ -53,7 +53,7 @@ public:
   FunctionValues(const FunctionValues& values);
   /// Reset the values to match a new domain.
   void reset(const FunctionDomain& domain);
-  /// Return the number of points, values, etc in the domain
+  /// Return the number of values
   size_t size() const {return m_calculated.size();}
   /// store i-th calculated value. 0 <= i < size()
   void setCalculated(size_t i,double value) {m_calculated[i] = value;}
@@ -63,6 +63,7 @@ public:
   double getCalculated(size_t i) const {return m_calculated[i];}
   double operator[](size_t i) const {return m_calculated[i];}
   void addToCalculated(size_t i, double value) {m_calculated[i] += value;}
+  void addToCalculated(size_t i, const FunctionValues& values);
   /// Get a pointer to calculated data at index i
   double* getPointerToCalculated(size_t i);
   /// Add other calculated values
diff --git a/Code/Mantid/Framework/API/inc/MantidAPI/JointDomain.h b/Code/Mantid/Framework/API/inc/MantidAPI/JointDomain.h
new file mode 100644
index 00000000000..cb6d03740e4
--- /dev/null
+++ b/Code/Mantid/Framework/API/inc/MantidAPI/JointDomain.h
@@ -0,0 +1,64 @@
+#ifndef MANTID_API_JOINTDOMAIN_H_
+#define MANTID_API_JOINTDOMAIN_H_
+
+//----------------------------------------------------------------------
+// Includes
+//----------------------------------------------------------------------
+#include "MantidAPI/DllConfig.h"
+#include "MantidAPI/CompositeDomain.h"
+
+#include <stdexcept>
+#include <vector>
+#include <algorithm>
+
+namespace Mantid
+{
+namespace API
+{
+/** Base class that represents the domain of a function.
+    A domain is a generalisation of x (argument) and y (value) arrays.
+    A domain consists at least of a list of function arguments for which a function should 
+    be evaluated and a buffer for the calculated values. If used in fitting also contains
+    the fit data and weights.
+
+    @author Roman Tolchenov, Tessella plc
+    @date 15/11/2011
+
+    Copyright &copy; 2009 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory
+
+    This file is part of Mantid.
+
+    Mantid 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.
+
+    Mantid is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+    File change history is stored at: <https://svn.mantidproject.org/mantid/trunk/Code/Mantid>.
+    Code Documentation is available at: <http://doxygen.mantidproject.org>
+*/
+class MANTID_API_DLL JointDomain: public CompositeDomain
+{
+public:
+  /// Return the number of points in the domain
+  virtual size_t size() const;
+  /// Return the number of parts in the domain
+  virtual size_t getNParts() const;
+  /// Return i-th domain
+  virtual const FunctionDomain& getDomain(size_t i) const;
+  void addDomain(FunctionDomain_sptr domain);
+protected:
+  std::vector< FunctionDomain_sptr > m_domains;
+};
+
+} // namespace API
+} // namespace Mantid
+
+#endif /*MANTID_API_JOINTDOMAIN_H_*/
diff --git a/Code/Mantid/Framework/API/inc/MantidAPI/MultiDomainFunction.h b/Code/Mantid/Framework/API/inc/MantidAPI/MultiDomainFunction.h
new file mode 100644
index 00000000000..2607e1561f9
--- /dev/null
+++ b/Code/Mantid/Framework/API/inc/MantidAPI/MultiDomainFunction.h
@@ -0,0 +1,80 @@
+#ifndef MANTID_API_MULTIDOMAINFUNCTION_H_
+#define MANTID_API_MULTIDOMAINFUNCTION_H_
+
+//----------------------------------------------------------------------
+// Includes
+//----------------------------------------------------------------------
+#include "MantidAPI/CompositeFunction.h"
+
+#include <map>
+
+namespace Mantid
+{
+namespace API
+{
+  class CompositeDomain;
+/** A composite function defined on a CompositeDomain. Member functions can be applied to 
+    one or more member domains of the CompositeDomain. If two functions applied to the same domain
+    the results are added (+).
+
+    @author Roman Tolchenov, Tessella plc
+    @date 13/03/2012
+
+    Copyright &copy; 2009 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory
+
+    This file is part of Mantid.
+
+    Mantid 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.
+
+    Mantid is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+    File change history is stored at: <https://svn.mantidproject.org/mantid/trunk/Code/Mantid>.
+    Code Documentation is available at: <http://doxygen.mantidproject.org>
+*/
+class MANTID_API_DLL MultiDomainFunction : public CompositeFunction
+{
+public:
+  /// Constructor
+  MultiDomainFunction():m_nDomains(0),m_maxIndex(0){}
+
+  /// Function you want to fit to. 
+  /// @param domain :: The buffer for writing the calculated values. Must be big enough to accept dataSize() values
+  virtual void function(const FunctionDomain& domain, FunctionValues& values)const;
+  /// Derivatives of function with respect to active parameters
+  virtual void functionDeriv(const FunctionDomain& domain, Jacobian& jacobian);
+
+  /// Associate a function and a domain
+  void setDomainIndex(size_t funIndex, size_t domainIndex);
+
+  /// Associate a function and a list of domains
+  void setDomainIndices(size_t funIndex, const std::vector<size_t>& domainIndices);
+
+  /// Clear all domain indices
+  void clearDomainIndices();
+protected:
+
+  /// Counts number of the domains
+  void countNumberOfDomains();
+  void countValueOffsets(const CompositeDomain& domain)const;
+  /// Domain index map: finction -> domain
+  std::map<size_t, std::vector<size_t> > m_domains;
+  /// Number of domains this MultiDomainFunction operates on. == number of different values in m_domains
+  size_t m_nDomains;
+  /// Maximum domain index
+  size_t m_maxIndex;
+  mutable std::vector<size_t> m_valueOffsets;
+};
+
+} // namespace API
+} // namespace Mantid
+
+#endif /*MANTID_API_MULTIDOMAINFUNCTION_H_*/
diff --git a/Code/Mantid/Framework/API/src/CompositeFunction.cpp b/Code/Mantid/Framework/API/src/CompositeFunction.cpp
index db71f7b0e22..831aa49c922 100644
--- a/Code/Mantid/Framework/API/src/CompositeFunction.cpp
+++ b/Code/Mantid/Framework/API/src/CompositeFunction.cpp
@@ -126,7 +126,6 @@ void CompositeFunction::function(const FunctionDomain& domain, FunctionValues& v
   values.zeroCalculated();
   for(size_t iFun = 0; iFun < nFunctions(); ++iFun)
   {
-    domain.reset();
     m_functions[ iFun ]->function(domain,tmp);
     values += tmp;
   }
@@ -141,7 +140,6 @@ void CompositeFunction::functionDeriv(const FunctionDomain& domain, Jacobian& ja
 {
   for(size_t iFun = 0; iFun < nFunctions(); ++iFun)
   {
-    domain.reset();
     PartialJacobian J(&jacobian,paramOffset(iFun));
     getFunction(iFun)->functionDeriv(domain,J);
   }
diff --git a/Code/Mantid/Framework/API/src/FunctionValues.cpp b/Code/Mantid/Framework/API/src/FunctionValues.cpp
index 3ac0d60fc32..6936e349067 100644
--- a/Code/Mantid/Framework/API/src/FunctionValues.cpp
+++ b/Code/Mantid/Framework/API/src/FunctionValues.cpp
@@ -93,6 +93,23 @@ namespace API
     setCalculated(0.0);
   }
 
+  /**
+   * Add values starting at index i.
+   */
+  void FunctionValues::addToCalculated(size_t i, const FunctionValues& values)
+  {
+    if (i + values.size() > size())
+    {
+      throw std::runtime_error("Cannot add function values: different sizes.");
+    }
+    std::transform(
+      m_calculated.begin() + i,
+      m_calculated.begin() + i + values.size(),
+      values.m_calculated.begin(),
+      m_calculated.begin() + i,
+      std::plus<double>()
+    );
+  }
 
   /**
    * Set a fitting data value.
diff --git a/Code/Mantid/Framework/API/src/JointDomain.cpp b/Code/Mantid/Framework/API/src/JointDomain.cpp
new file mode 100644
index 00000000000..d031a3f3678
--- /dev/null
+++ b/Code/Mantid/Framework/API/src/JointDomain.cpp
@@ -0,0 +1,35 @@
+//----------------------------------------------------------------------
+// Includes
+//----------------------------------------------------------------------
+#include "MantidAPI/JointDomain.h"
+
+namespace Mantid
+{
+namespace API
+{
+  /// Return the number of points in the domain
+  size_t JointDomain::size() const
+  {
+    size_t n = 0;
+    std::for_each(m_domains.begin(),m_domains.end(),[&n](const FunctionDomain_sptr d){
+      n += d->size();
+    });
+    return n;
+  }
+  /// Return the number of parts in the domain
+  size_t JointDomain::getNParts() const
+  {
+    return m_domains.size();
+  }
+  /// Return i-th domain
+  const FunctionDomain& JointDomain::getDomain(size_t i) const
+  {
+    return *m_domains.at(i);
+  }
+  void JointDomain::addDomain(FunctionDomain_sptr domain)
+  {
+    m_domains.push_back(domain);
+  }
+  
+} // namespace API
+} // namespace Mantid
diff --git a/Code/Mantid/Framework/API/src/MultiDomainFunction.cpp b/Code/Mantid/Framework/API/src/MultiDomainFunction.cpp
new file mode 100644
index 00000000000..a2a4f5632dc
--- /dev/null
+++ b/Code/Mantid/Framework/API/src/MultiDomainFunction.cpp
@@ -0,0 +1,174 @@
+//----------------------------------------------------------------------
+// Includes
+//----------------------------------------------------------------------
+#include "MantidAPI/MultiDomainFunction.h"
+#include "MantidAPI/CompositeDomain.h"
+
+#include <boost/lexical_cast.hpp>
+#include <set>
+
+namespace Mantid
+{
+namespace API
+{
+
+  /**
+   * Associate a member function and a domain. The function will only be applied
+   * to this domain.
+   * @param funIndex :: Index of a member function.
+   * @param domainIndex :: Index of a domain to be associated with the function.
+   */
+  void MultiDomainFunction::setDomainIndex(size_t funIndex, size_t domainIndex)
+  {
+    m_domains[funIndex] = std::vector<size_t>(1,domainIndex);
+    countNumberOfDomains();
+  }
+
+  /**
+   * Associate a member function and a list of domains. The function will only be applied
+   * to the listed domains.
+   * @param funIndex :: Index of a member function.
+   * @param domainIndex :: A vector with indices of domains to be associated with the function.
+   */
+  void MultiDomainFunction::setDomainIndices(size_t funIndex, const std::vector<size_t>& domainIndices)
+  {
+    m_domains[funIndex] = domainIndices;
+    countNumberOfDomains();
+  }
+
+  /**
+   * Counts number of the domains.
+   */
+  void MultiDomainFunction::countNumberOfDomains()
+  {
+    std::set<size_t> dSet;
+    for(auto it = m_domains.begin(); it != m_domains.end(); ++it)
+    {
+      if (it->second.size())
+      {
+        dSet.insert(it->second.begin(),it->second.end());
+      }
+    }
+    m_nDomains = dSet.size();
+    m_maxIndex = dSet.empty() ? 0 : *dSet.rbegin();
+  }
+
+  /**
+   * Count value offsets for each member domain in a CompositeDomain.
+   */
+  void MultiDomainFunction::countValueOffsets(const CompositeDomain& domain)const
+  {
+    m_valueOffsets.clear();
+    m_valueOffsets.push_back(0);
+    for(size_t i = 0; i < domain.getNParts(); ++i)
+    {
+      const FunctionDomain& d = domain.getDomain(i);
+      m_valueOffsets.push_back(m_valueOffsets.back() + d.size());
+    }
+  }
+
+  /// Clear all domain indices
+  void MultiDomainFunction::clearDomainIndices()
+  {
+    m_domains.clear();
+    countNumberOfDomains();
+  }
+
+  /// Function you want to fit to. 
+  /// @param domain :: The buffer for writing the calculated values. Must be big enough to accept dataSize() values
+  void MultiDomainFunction::function(const FunctionDomain& domain, FunctionValues& values)const
+  {
+    // works only on CompositeDomain
+    if (!dynamic_cast<const CompositeDomain*>(&domain))
+    {
+      throw std::invalid_argument("Non-CompositeDomain passed to MultiDomainFunction.");
+    }
+    const CompositeDomain& cd = dynamic_cast<const CompositeDomain&>(domain);
+    // domain must not have less parts than m_maxIndex
+    if (cd.getNParts() < m_maxIndex)
+    {
+      throw std::invalid_argument("CompositeDomain has too few parts (" 
+        + boost::lexical_cast<std::string>(cd.getNParts()) +
+        ") for MultiDomainFunction (max index " + boost::lexical_cast<std::string>(m_maxIndex) + ").");
+    }
+    // domain and values must be consistent
+    if (cd.size() != values.size())
+    {
+      throw std::invalid_argument("MultiDomainFunction: domain and values have different sizes.");
+    }
+
+    // evaluate member functions
+    values.zeroCalculated();
+    for(size_t iFun = 0; iFun < nFunctions(); ++iFun)
+    {
+      // find the domains member function must be applied to
+      std::vector<size_t> domains;
+      auto it = m_domains.find(iFun);
+      if (it == m_domains.end())
+      {// apply to all domains
+        domains.resize(cd.getNParts());
+        size_t i = 0;
+        std::generate(domains.begin(),domains.end(),[&i]()->size_t{return i++;});
+      }
+      else
+      {// apply to selected domains
+        domains.assign(it->second.begin(),it->second.end());
+      }
+
+      countValueOffsets(cd);
+      for(auto i = domains.begin(); i != domains.end(); ++i)
+      {
+        const FunctionDomain& d = cd.getDomain(*i);
+        FunctionValues tmp(d);
+        getFunction(iFun)->function( d, tmp );
+        values.addToCalculated(m_valueOffsets[*i],tmp);
+      }
+    }
+  }
+
+  /// Derivatives of function with respect to active parameters
+  void MultiDomainFunction::functionDeriv(const FunctionDomain& domain, Jacobian& jacobian)
+  {
+    // works only on CompositeDomain
+    if (!dynamic_cast<const CompositeDomain*>(&domain))
+    {
+      throw std::invalid_argument("Non-CompositeDomain passed to MultiDomainFunction.");
+    }
+    const CompositeDomain& cd = dynamic_cast<const CompositeDomain&>(domain);
+    // domain must not have less parts than m_maxIndex
+    if (cd.getNParts() < m_maxIndex)
+    {
+      throw std::invalid_argument("CompositeDomain has too few parts (" 
+        + boost::lexical_cast<std::string>(cd.getNParts()) +
+        ") for MultiDomainFunction (max index " + boost::lexical_cast<std::string>(m_maxIndex) + ").");
+    }
+
+    // evaluate member functions derivatives
+    for(size_t iFun = 0; iFun < nFunctions(); ++iFun)
+    {
+      // find the domains member function must be applied to
+      std::vector<size_t> domains;
+      auto it = m_domains.find(iFun);
+      if (it == m_domains.end())
+      {// apply to all domains
+        domains.resize(cd.getNParts());
+        size_t i = 0;
+        std::generate(domains.begin(),domains.end(),[&i]()->size_t{return i++;});
+      }
+      else
+      {// apply to selected domains
+        domains.assign(it->second.begin(),it->second.end());
+      }
+
+      countValueOffsets(cd);
+      for(auto i = domains.begin(); i != domains.end(); ++i)
+      {
+        const FunctionDomain& d = cd.getDomain(*i);
+        PartialJacobian J(&jacobian,m_valueOffsets[*i],paramOffset(iFun));
+        getFunction(iFun)->functionDeriv(d,J);
+      }
+    }
+  }
+
+} // namespace API
+} // namespace Mantid
diff --git a/Code/Mantid/Framework/API/test/MultiDomainFunctionTest.h b/Code/Mantid/Framework/API/test/MultiDomainFunctionTest.h
new file mode 100644
index 00000000000..a255a9911dc
--- /dev/null
+++ b/Code/Mantid/Framework/API/test/MultiDomainFunctionTest.h
@@ -0,0 +1,245 @@
+#ifndef MULTIDOMAINFUNCTIONTEST_H_
+#define MULTIDOMAINFUNCTIONTEST_H_
+
+#include "MantidAPI/FunctionDomain1D.h"
+#include "MantidAPI/FunctionValues.h"
+#include "MantidAPI/MultiDomainFunction.h"
+#include "MantidAPI/JointDomain.h"
+#include "MantidAPI/IFunction1D.h"
+#include "MantidAPI/ParamFunction.h"
+
+#include <cxxtest/TestSuite.h>
+#include <boost/make_shared.hpp>
+#include <algorithm>
+#include <iostream>
+
+using namespace Mantid;
+using namespace Mantid::API;
+
+class MultiDomainFunctionTest_Function: public virtual IFunction1D, public virtual ParamFunction
+{
+public:
+  MultiDomainFunctionTest_Function():IFunction1D(),ParamFunction()
+  {
+    this->declareParameter("A",0);
+    this->declareParameter("B",0);
+  }
+  virtual std::string name() const {return "MultiDomainFunctionTest_Function";}
+protected:
+  virtual void function1D(double* out, const double* xValues, const size_t nData)const
+  {
+    const double A = getParameter(0);
+    const double B = getParameter(1);
+
+    for(size_t i = 0; i < nData; ++i)
+    {
+      double x = xValues[i];
+      out[i] = A + B * x;
+    }
+  }
+  virtual void functionDeriv1D(Jacobian* out, const double* xValues, const size_t nData)
+  {
+    for(size_t i = 0; i < nData; ++i)
+    {
+      double x = xValues[i];
+      out->set(i,0,x);
+      out->set(i,1,1.0);
+    }
+  }
+};
+
+class MultiDomainFunctionTest : public CxxTest::TestSuite
+{
+public:
+  MultiDomainFunctionTest()
+  {
+    multi.addFunction(boost::make_shared<MultiDomainFunctionTest_Function>());
+    multi.addFunction(boost::make_shared<MultiDomainFunctionTest_Function>());
+    multi.addFunction(boost::make_shared<MultiDomainFunctionTest_Function>());
+
+    multi.getFunction(0)->setParameter("A",0);
+    multi.getFunction(0)->setParameter("B",1);
+
+    multi.getFunction(1)->setParameter("A",1);
+    multi.getFunction(1)->setParameter("B",2);
+
+    multi.getFunction(2)->setParameter("A",2);
+    multi.getFunction(2)->setParameter("B",3);
+
+    domain.addDomain(boost::make_shared<FunctionDomain1D>(0,1,9));
+    domain.addDomain(boost::make_shared<FunctionDomain1D>(1,2,10));
+    domain.addDomain(boost::make_shared<FunctionDomain1D>(2,3,11));
+
+  }
+
+  void test_calc_domain0_only()
+  {
+    multi.setDomainIndex(0,0);
+    multi.setDomainIndices(1,std::vector<size_t>());
+    multi.setDomainIndices(2,std::vector<size_t>());
+    //multi.setDomainIndex(1,1);
+    //multi.setDomainIndex(2,2);
+
+    //std::vector<size_t> ii;
+    //ii.push_back(0);
+    //ii.push_back(1);
+    //multi.setDomainIndices(1,ii);
+    //ii.clear();
+    //ii.push_back(0);
+    //ii.push_back(2);
+    //multi.setDomainIndices(2,ii);
+
+    FunctionValues values(domain);
+    multi.function(domain,values);
+
+    const double A = multi.getFunction(0)->getParameter("A");
+    const double B = multi.getFunction(0)->getParameter("B");
+    auto d = static_cast<const FunctionDomain1D&>(domain.getDomain(0));
+    for(size_t i = 0; i < 9; ++i)
+    {
+      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d[i]);
+    }
+    for(size_t i = 9; i < 19; ++i)
+    {
+      TS_ASSERT_EQUALS(values.getCalculated(i), 0);
+    }
+    for(size_t i = 19; i < 30; ++i)
+    {
+      TS_ASSERT_EQUALS(values.getCalculated(i), 0);
+    }
+
+  }
+
+  void test_calc_domain1_only()
+  {
+    multi.setDomainIndex(0,1);
+    multi.setDomainIndices(1,std::vector<size_t>());
+    multi.setDomainIndices(2,std::vector<size_t>());
+
+    FunctionValues values(domain);
+    multi.function(domain,values);
+
+    const double A = multi.getFunction(0)->getParameter("A");
+    const double B = multi.getFunction(0)->getParameter("B");
+    auto d = static_cast<const FunctionDomain1D&>(domain.getDomain(1));
+    for(size_t i = 0; i < 9; ++i)
+    {
+      TS_ASSERT_EQUALS(values.getCalculated(i), 0);
+    }
+    for(size_t i = 9; i < 19; ++i)
+    {
+      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d[i-9]);
+    }
+    for(size_t i = 19; i < 30; ++i)
+    {
+      TS_ASSERT_EQUALS(values.getCalculated(i), 0);
+    }
+  }
+
+  void test_calc_domain2_only()
+  {
+    multi.setDomainIndex(0,2);
+    multi.setDomainIndices(1,std::vector<size_t>());
+    multi.setDomainIndices(2,std::vector<size_t>());
+
+    FunctionValues values(domain);
+    multi.function(domain,values);
+
+    const double A = multi.getFunction(0)->getParameter("A");
+    const double B = multi.getFunction(0)->getParameter("B");
+    auto d = static_cast<const FunctionDomain1D&>(domain.getDomain(2));
+    for(size_t i = 0; i < 9; ++i)
+    {
+      TS_ASSERT_EQUALS(values.getCalculated(i), 0);
+    }
+    for(size_t i = 9; i < 19; ++i)
+    {
+      TS_ASSERT_EQUALS(values.getCalculated(i), 0);
+    }
+    for(size_t i = 19; i < 30; ++i)
+    {
+      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d[i-19]);
+    }
+
+  }
+
+  void test_calc_all_domains()
+  {
+    multi.clearDomainIndices();
+    multi.setDomainIndices(1,std::vector<size_t>());
+    multi.setDomainIndices(2,std::vector<size_t>());
+
+    FunctionValues values(domain);
+    multi.function(domain,values);
+
+    const double A = multi.getFunction(0)->getParameter("A");
+    const double B = multi.getFunction(0)->getParameter("B");
+    auto d0 = static_cast<const FunctionDomain1D&>(domain.getDomain(0));
+    for(size_t i = 0; i < 9; ++i)
+    {
+      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d0[i]);
+    }
+    auto d1 = static_cast<const FunctionDomain1D&>(domain.getDomain(1));
+    for(size_t i = 9; i < 19; ++i)
+    {
+      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d1[i-9]);
+    }
+    auto d2 = static_cast<const FunctionDomain1D&>(domain.getDomain(2));
+    for(size_t i = 19; i < 30; ++i)
+    {
+      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d2[i-19]);
+    }
+
+  }
+
+  void test_calc()
+  {
+    multi.setDomainIndex(0,0);
+    std::vector<size_t> ii;
+    ii.push_back(0);
+    ii.push_back(1);
+    multi.setDomainIndices(1,ii);
+    ii.clear();
+    ii.push_back(0);
+    ii.push_back(2);
+    multi.setDomainIndices(2,ii);
+
+    FunctionValues values(domain);
+    multi.function(domain,values);
+
+    double A = multi.getFunction(0)->getParameter("A") + 
+      multi.getFunction(1)->getParameter("A") + 
+      multi.getFunction(2)->getParameter("A");
+    double B = multi.getFunction(0)->getParameter("B") + 
+      multi.getFunction(1)->getParameter("B") + 
+      multi.getFunction(2)->getParameter("B");
+    auto d0 = static_cast<const FunctionDomain1D&>(domain.getDomain(0));
+    for(size_t i = 0; i < 9; ++i)
+    {
+      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d0[i]);
+    }
+
+    A = multi.getFunction(1)->getParameter("A");
+    B = multi.getFunction(1)->getParameter("B");
+    auto d1 = static_cast<const FunctionDomain1D&>(domain.getDomain(1));
+    for(size_t i = 9; i < 19; ++i)
+    {
+      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d1[i-9]);
+    }
+
+    A = multi.getFunction(2)->getParameter("A");
+    B = multi.getFunction(2)->getParameter("B");
+    auto d2 = static_cast<const FunctionDomain1D&>(domain.getDomain(2));
+    for(size_t i = 19; i < 30; ++i)
+    {
+      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d2[i-19]);
+    }
+
+  }
+
+private:
+  MultiDomainFunction multi;
+  JointDomain domain;
+};
+
+#endif /*MULTIDOMAINFUNCTIONTEST_H_*/
diff --git a/Code/Mantid/Framework/CurveFitting/CMakeLists.txt b/Code/Mantid/Framework/CurveFitting/CMakeLists.txt
index 2be6cef1ee6..902104b9471 100644
--- a/Code/Mantid/Framework/CurveFitting/CMakeLists.txt
+++ b/Code/Mantid/Framework/CurveFitting/CMakeLists.txt
@@ -168,6 +168,7 @@ set ( TEST_FILES
 	test/LogNormalTest.h
 	test/Lorentzian1DTest.h
 	test/LorentzianTest.h
+	test/MultiDomainFunctionTest.h
 #	test/MultiBGTest.h
 	test/PlotPeakByLogValueTest.h
 	test/PRConjugateGradientTest.h
diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CostFuncLeastSquares.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CostFuncLeastSquares.h
index bb070e8d1ef..97495df7b40 100644
--- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CostFuncLeastSquares.h
+++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CostFuncLeastSquares.h
@@ -77,6 +77,15 @@ protected:
 
   virtual void calActiveCovarianceMatrix(GSLMatrix& covar, double epsrel = 1e-8);
 
+  void addVal(
+    API::FunctionDomain_sptr domain,
+    API::FunctionValues_sptr values
+    )const;
+  void addValDerivHessian(
+    API::FunctionDomain_sptr domain,
+    API::FunctionValues_sptr values,
+    bool evalFunction = true, bool evalDeriv = true, bool evalHessian = true) const;
+
 private:
 
   mutable double m_value;
diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/GSLVector.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/GSLVector.h
index cdb7d405ce4..c54cf4f6839 100644
--- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/GSLVector.h
+++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/GSLVector.h
@@ -109,6 +109,12 @@ namespace Mantid
       throw std::out_of_range("GSLVector index is out of range.");
     }
 
+    // Set all elements to zero
+    void zero()
+    {
+      gsl_vector_set_zero( m_vector );
+    }
+
     GSLVector& operator+=(const GSLVector& v)
     {
       gsl_vector_add(m_vector, v.gsl());
diff --git a/Code/Mantid/Framework/CurveFitting/src/CostFuncLeastSquares.cpp b/Code/Mantid/Framework/CurveFitting/src/CostFuncLeastSquares.cpp
index cd5f3369183..dfd85fc99f6 100644
--- a/Code/Mantid/Framework/CurveFitting/src/CostFuncLeastSquares.cpp
+++ b/Code/Mantid/Framework/CurveFitting/src/CostFuncLeastSquares.cpp
@@ -4,6 +4,7 @@
 #include "MantidCurveFitting/CostFuncLeastSquares.h"
 #include "MantidCurveFitting/Jacobian.h"
 #include "MantidAPI/IConstraint.h"
+#include "MantidAPI/CompositeDomain.h"
 
 namespace
 {
@@ -24,34 +25,52 @@ double CostFuncLeastSquares::val() const
   if ( !m_dirtyVal ) return m_value;
 
   checkValidity();
-  m_domain->reset();
-  m_function->function(*m_domain,*m_values);
-  size_t ny = m_values->size();
 
-  double retVal = 0.0;
+  m_value = 0.0;
 
-  for (size_t i = 0; i < ny; i++)
+  auto compDomain = boost::dynamic_pointer_cast<API::CompositeDomain>(m_domain);
+
+  //if (compDomain)
+  //{
+  //}
+  //else
   {
-    double val = ( m_values->getCalculated(i) - m_values->getFitData(i) ) * m_values->getFitWeight(i);
-    retVal += val * val;
+    addVal(m_domain,m_values);
   }
-  
-  retVal *= 0.5;
 
+  // add penalty
   for(size_t i=0;i<m_function->nParams();++i)
   {
     if ( !m_function->isActive(i) ) continue;
     API::IConstraint* c = m_function->getConstraint(i);
     if (c)
     {
-      retVal += c->check();
+      m_value += c->check();
     }
   }
 
   m_dirtyVal = false;
-  return retVal;
+  return m_value;
 }
 
+void CostFuncLeastSquares::addVal(API::FunctionDomain_sptr domain, API::FunctionValues_sptr values)const
+{
+  m_function->function(*m_domain,*m_values);
+  size_t ny = m_values->size();
+
+  double retVal = 0.0;
+
+  for (size_t i = 0; i < ny; i++)
+  {
+    double val = ( m_values->getCalculated(i) - m_values->getFitData(i) ) * m_values->getFitWeight(i);
+    retVal += val * val;
+  }
+  
+  m_value += 0.5 * retVal;
+
+}
+
+
 /// Calculate the derivatives of the cost function
 /// @param der :: Container to output the derivatives
 void CostFuncLeastSquares::deriv(std::vector<double>& der) const
@@ -104,21 +123,102 @@ double CostFuncLeastSquares::valDerivHessian(bool evalFunction, bool evalDeriv,
   if (m_dirtyVal) evalFunction = true;
 
   checkValidity();
+
+  if (evalFunction)
+  {
+    m_value = 0.0;
+  }
+  if (evalDeriv)
+  {
+    m_der.resize(nParams());
+    m_der.zero();
+  }
+  if (evalHessian)
+  {
+    m_hessian.resize(nParams(),nParams());
+    m_hessian.zero();
+  }
+
+  auto compDomain = boost::dynamic_pointer_cast<API::CompositeDomain>(m_domain);
+
+  //if (compDomain)
+  //{
+  //}
+  //else
+  {
+    addValDerivHessian(m_domain,m_values,evalFunction,evalDeriv,evalHessian);
+  }
+
+  // Add constraints penaly
+  size_t np = m_function->nParams();
+  if (evalFunction)
+  {
+    for(size_t i = 0; i < np; ++i)
+    {
+      API::IConstraint* c = m_function->getConstraint(i);
+      if (c)
+      {
+        m_value += c->check();
+      }
+    }
+    m_dirtyVal = false;
+  }
+
+  if (evalDeriv)
+  {
+    size_t i = 0;
+    for(size_t ip = 0; ip < np; ++ip)
+    {
+      API::IConstraint* c = m_function->getConstraint(ip);
+      if (c)
+      {
+        if ( !m_function->isActive(ip) ) continue;
+        double d =  m_der.get(i) + c->checkDeriv();
+        m_der.set(i,d);
+      }
+      ++i;
+    }
+    m_dirtyDeriv = false;
+  }
+
+  if (evalDeriv)
+  {
+    size_t i = 0;
+    for(size_t ip = 0; ip < np; ++ip)
+    {
+      API::IConstraint* c = m_function->getConstraint(ip);
+      if (c)
+      {
+        if ( !m_function->isActive(ip) ) continue;
+        double d =  m_hessian.get(i,i) + c->checkDeriv2();
+        m_hessian.set(i,i,d);
+      }
+      ++i;
+    }
+    m_dirtyHessian = false;
+  }
+
+  return m_value;
+}
+
+/**
+ * Update the cost function, derivatives and hessian by adding values calculated
+ * on a domain.
+ */
+void CostFuncLeastSquares::addValDerivHessian(
+  API::FunctionDomain_sptr domain,
+  API::FunctionValues_sptr values,
+  bool evalFunction , bool evalDeriv, bool evalHessian) const
+{
   size_t np = m_function->nParams();  // number of parameters 
   size_t ny = m_domain->size(); // number of data points
   Jacobian jacobian(ny,np);
   if (evalFunction)
   {
-    m_domain->reset();
-    m_function->function(*m_domain,*m_values);
+    m_function->function(*domain,*values);
   }
-  m_domain->reset();
-  m_function->functionDeriv(*m_domain,jacobian);
+  m_function->functionDeriv(*domain,jacobian);
 
-  if (m_der.size() != nParams())
-  {
-    m_der.resize(nParams());
-  }
   size_t iActiveP = 0;
   double fVal = 0.0;
   if (debug)
@@ -137,7 +237,7 @@ double CostFuncLeastSquares::valDerivHessian(bool evalFunction, bool evalDeriv,
   for(size_t ip = 0; ip < np; ++ip)
   {
     if ( !m_function->isActive(ip) ) continue;
-    double d = 0.0;
+    double d = m_der.get(iActiveP);
     for(size_t i = 0; i < ny; ++i)
     {
       double calc = m_values->getCalculated(i);
@@ -150,11 +250,6 @@ double CostFuncLeastSquares::valDerivHessian(bool evalFunction, bool evalDeriv,
         fVal += y * y;
       }
     }
-    API::IConstraint* c = m_function->getConstraint(ip);
-    if (c)
-    {
-      d += c->checkDeriv();
-    }
     m_der.set(iActiveP, d);
     //std::cerr << "der " << ip << ' ' << der[iActiveP] << std::endl;
     ++iActiveP;
@@ -162,27 +257,12 @@ double CostFuncLeastSquares::valDerivHessian(bool evalFunction, bool evalDeriv,
 
   if (evalFunction)
   {
-    fVal *= 0.5;
-    for(size_t i = 0; i < np; ++i)
-    {
-      API::IConstraint* c = m_function->getConstraint(i);
-      if (c)
-      {
-        fVal += c->check();
-      }
-    }
-    m_value = fVal;
-    m_dirtyVal = false;
+    m_value += 0.5 * fVal;
   }
-  m_dirtyDeriv = false;
 
-  if (!evalHessian) return m_value;
+  if (!evalHessian) return;
 
   size_t na = m_der.size(); // number of active parameters
-  if (m_hessian.size1() != na || m_hessian.size2() != na)
-  {
-    m_hessian.resize(na,na);
-  }
 
   size_t i1 = 0; // active parameter index
   for(size_t i = 0; i < np; ++i) // over parameters
@@ -192,20 +272,12 @@ double CostFuncLeastSquares::valDerivHessian(bool evalFunction, bool evalDeriv,
     for(size_t j = 0; j <= i; ++j) // over ~ half of parameters
     {
       if ( !m_function->isActive(j) ) continue;
-      double d = 0.0;
+      double d = m_hessian.get(i1,i2);
       for(size_t k = 0; k < ny; ++k) // over fitting data
       {
         double w = m_values->getFitWeight(k);
         d += jacobian.get(k,i) * jacobian.get(k,j) * w * w;
       }
-      if (i == j)
-      {
-        API::IConstraint* c = m_function->getConstraint(i);
-        if (c)
-        {
-          d += c->checkDeriv2();
-        }
-      }
       m_hessian.set(i1,i2,d);
       //std::cerr << "hess " << i1 << ' ' << i2 << std::endl;
       if (i1 != i2)
@@ -216,11 +288,9 @@ double CostFuncLeastSquares::valDerivHessian(bool evalFunction, bool evalDeriv,
     }
     ++i1;
   }
-
-  m_dirtyHessian = false;
-  return m_value;
 }
 
+
 const GSLVector& CostFuncLeastSquares::getDeriv() const
 {
   if (m_pushed)
diff --git a/Code/Mantid/Framework/CurveFitting/src/LevenbergMarquardtMDMinimizer.cpp b/Code/Mantid/Framework/CurveFitting/src/LevenbergMarquardtMDMinimizer.cpp
index 0300d837aa0..56bbbe4834b 100644
--- a/Code/Mantid/Framework/CurveFitting/src/LevenbergMarquardtMDMinimizer.cpp
+++ b/Code/Mantid/Framework/CurveFitting/src/LevenbergMarquardtMDMinimizer.cpp
@@ -33,6 +33,7 @@ m_mu(0),
 m_nu(2.0),
 m_rho(1.0)
 {
+  gsl_set_error_handler_off();
 }
 
 /// Initialize minimizer, i.e. pass a function to minimize.
diff --git a/Code/Mantid/Framework/CurveFitting/test/MultiDomainFunctionTest.h b/Code/Mantid/Framework/CurveFitting/test/MultiDomainFunctionTest.h
new file mode 100644
index 00000000000..546bb7091fc
--- /dev/null
+++ b/Code/Mantid/Framework/CurveFitting/test/MultiDomainFunctionTest.h
@@ -0,0 +1,140 @@
+#ifndef MULTIDOMAINFUNCTIONTEST_H_
+#define MULTIDOMAINFUNCTIONTEST_H_
+
+#include "MantidAPI/FunctionDomain1D.h"
+#include "MantidAPI/FunctionValues.h"
+#include "MantidAPI/MultiDomainFunction.h"
+#include "MantidAPI/JointDomain.h"
+#include "MantidAPI/IFunction1D.h"
+#include "MantidAPI/ParamFunction.h"
+#include "MantidCurveFitting/CostFuncLeastSquares.h"
+#include "MantidCurveFitting/LevenbergMarquardtMDMinimizer.h"
+
+#include <cxxtest/TestSuite.h>
+#include <boost/make_shared.hpp>
+#include <algorithm>
+#include <iostream>
+
+using namespace Mantid;
+using namespace Mantid::API;
+using namespace Mantid::CurveFitting;
+
+class MultiDomainFunctionTest_Function: public virtual IFunction1D, public virtual ParamFunction
+{
+public:
+  MultiDomainFunctionTest_Function():IFunction1D(),ParamFunction()
+  {
+    this->declareParameter("A",0);
+    this->declareParameter("B",0);
+  }
+  virtual std::string name() const {return "MultiDomainFunctionTest_Function";}
+protected:
+  virtual void function1D(double* out, const double* xValues, const size_t nData)const
+  {
+    const double A = getParameter(0);
+    const double B = getParameter(1);
+
+    for(size_t i = 0; i < nData; ++i)
+    {
+      double x = xValues[i];
+      out[i] = A + B * x;
+    }
+  }
+  virtual void functionDeriv1D(Jacobian* out, const double* xValues, const size_t nData)
+  {
+    for(size_t i = 0; i < nData; ++i)
+    {
+      double x = xValues[i];
+      out->set(i,1,x);
+      out->set(i,0,1.0);
+    }
+  }
+};
+
+class MultiDomainFunctionTest : public CxxTest::TestSuite
+{
+public:
+  MultiDomainFunctionTest()
+  {
+    multi = boost::make_shared<MultiDomainFunction>();
+    multi->addFunction(boost::make_shared<MultiDomainFunctionTest_Function>());
+    multi->addFunction(boost::make_shared<MultiDomainFunctionTest_Function>());
+    multi->addFunction(boost::make_shared<MultiDomainFunctionTest_Function>());
+
+    multi->getFunction(0)->setParameter("A",0);
+    multi->getFunction(0)->setParameter("B",0);
+
+    multi->getFunction(1)->setParameter("A",0);
+    multi->getFunction(1)->setParameter("B",0);
+
+    multi->getFunction(2)->setParameter("A",0);
+    multi->getFunction(2)->setParameter("B",0);
+
+    domain = boost::make_shared<JointDomain>();
+    domain->addDomain(boost::make_shared<FunctionDomain1D>(0,1,9));
+    domain->addDomain(boost::make_shared<FunctionDomain1D>(1,2,10));
+    domain->addDomain(boost::make_shared<FunctionDomain1D>(2,3,11));
+
+  }
+
+  void test_fit()
+  {
+
+    //system("pause");
+    auto values = boost::make_shared<FunctionValues>(*domain);
+    const double A0 = 0, A1 = 1, A2 = 2;
+    const double B0 = 1, B1 = 2, B2 = 3;
+
+    auto d0 = static_cast<const FunctionDomain1D&>(domain->getDomain(0));
+    for(size_t i = 0; i < d0.size(); ++i)
+    {
+      values->setFitData(i, A0 + A1 + A2 + (B0 + B1 + B2) * d0[i]);
+    }
+
+    auto d1 = static_cast<const FunctionDomain1D&>(domain->getDomain(1));
+    for(size_t i = 0; i < d1.size(); ++i)
+    {
+      values->setFitData(9 + i, A0 + A1 + (B0 + B1) * d1[i]);
+    }
+
+    auto d2 = static_cast<const FunctionDomain1D&>(domain->getDomain(2));
+    for(size_t i = 0; i < d2.size(); ++i)
+    {
+      values->setFitData(19 + i, A0 + A2 + (B0 + B2) * d2[i]);
+    }
+    values->setFitWeights(1);
+
+    multi->clearDomainIndices();
+    std::vector<size_t> ii(2);
+    ii[0] = 0;
+    ii[1] = 1;
+    multi->setDomainIndices(1,ii);
+    ii[0] = 0;
+    ii[1] = 2;
+    multi->setDomainIndices(2,ii);
+
+    boost::shared_ptr<CostFuncLeastSquares> costFun(new CostFuncLeastSquares);
+    costFun->setFittingFunction(multi,domain,values);
+    TS_ASSERT_EQUALS(costFun->nParams(),6);
+
+    LevenbergMarquardtMDMinimizer s;
+    s.initialize(costFun);
+    TS_ASSERT(s.minimize());
+
+    TS_ASSERT_EQUALS(s.getError(),"success");
+    TS_ASSERT_DELTA(s.costFunctionVal(),0,1e-4);
+    
+    TS_ASSERT_DELTA(multi->getFunction(0)->getParameter("A"),0,1e-8);
+    TS_ASSERT_DELTA(multi->getFunction(0)->getParameter("B"),1,1e-8);
+    TS_ASSERT_DELTA(multi->getFunction(1)->getParameter("A"),1,1e-8);
+    TS_ASSERT_DELTA(multi->getFunction(1)->getParameter("B"),2,1e-8);
+    TS_ASSERT_DELTA(multi->getFunction(2)->getParameter("A"),2,1e-8);
+    TS_ASSERT_DELTA(multi->getFunction(2)->getParameter("B"),3,1e-8);
+  }
+
+private:
+  boost::shared_ptr<MultiDomainFunction> multi;
+  boost::shared_ptr<JointDomain> domain;
+};
+
+#endif /*MULTIDOMAINFUNCTIONTEST_H_*/
-- 
GitLab