diff --git a/Fit/Parameters/AttLimits.cpp b/Fit/Parameters/AttLimits.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bf108f4f6bb7c602bedb91ffbefdc01b0990926c
--- /dev/null
+++ b/Fit/Parameters/AttLimits.cpp
@@ -0,0 +1,102 @@
+// ************************************************************************** //
+//
+//  BornAgain: simulate and fit scattering at grazing incidence
+//
+//! @file      Fit/Parameters/AttLimits.cpp
+//! @brief     Implements and implements class AttLimits.
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2015
+//! @authors   Scientific Computing Group at MLZ Garching
+//! @authors   C. Durniak, M. Ganeva, G. Pospelov, W. Van Herck, J. Wuttke
+//
+// ************************************************************************** //
+
+#include "AttLimits.h"
+
+
+AttLimits::AttLimits()
+    : m_limits(RealLimits::limitless())
+    , m_att_fixed(Attributes::free())
+{
+
+}
+
+AttLimits AttLimits::lowerLimited(double bound_value)
+{
+    return AttLimits(RealLimits::lowerLimited(bound_value), Attributes::free());
+}
+
+AttLimits AttLimits::positive()
+{
+    return AttLimits(RealLimits::positive(), Attributes::free());
+}
+
+AttLimits AttLimits::nonnegative()
+{
+    return AttLimits(RealLimits::nonnegative(), Attributes::free());
+}
+
+AttLimits AttLimits::upperLimited(double bound_value)
+{
+    return AttLimits(RealLimits::upperLimited(bound_value), Attributes::free());
+}
+
+AttLimits AttLimits::limited(double left_bound_value, double right_bound_value)
+{
+    return AttLimits(RealLimits::limited(left_bound_value, right_bound_value), Attributes::free());
+}
+
+AttLimits AttLimits::fixed()
+{
+    return AttLimits(RealLimits::limitless(), Attributes::fixed());
+}
+
+bool AttLimits::isFixed() const
+{
+    return m_att_fixed.isFixed();
+}
+
+bool AttLimits::isLimited() const
+{
+    return m_att_fixed.isFree() && m_limits.hasLowerAndUpperLimits();
+}
+
+bool AttLimits::isUpperLimited() const
+{
+    return m_att_fixed.isFree() && !m_limits.hasLowerLimit() && m_limits.hasUpperLimit();
+}
+
+bool AttLimits::isLowerLimited() const
+{
+    return m_att_fixed.isFree() && m_limits.hasLowerLimit() && !m_limits.hasUpperLimit();
+}
+
+bool AttLimits::isLimitless() const
+{
+    return m_att_fixed.isFree() && !m_limits.hasLowerLimit() && !m_limits.hasUpperLimit();
+}
+
+double AttLimits::lowerLimit() const
+{
+    return m_limits.getLowerLimit();
+}
+
+double AttLimits::upperLimit() const
+{
+    return m_limits.getUpperLimit();
+}
+
+void AttLimits::setFixed(bool isFixed)
+{
+    m_limits.removeLimits();
+    m_att_fixed.setFixed(isFixed);
+}
+
+AttLimits::AttLimits(const RealLimits &limits, const Attributes &fixedAttr)
+    : m_limits(limits)
+    , m_att_fixed(fixedAttr)
+{
+
+}
diff --git a/Fit/Parameters/AttLimits.h b/Fit/Parameters/AttLimits.h
new file mode 100644
index 0000000000000000000000000000000000000000..7b598f4363852b92ec2896febfb046448527b166
--- /dev/null
+++ b/Fit/Parameters/AttLimits.h
@@ -0,0 +1,58 @@
+// ************************************************************************** //
+//
+//  BornAgain: simulate and fit scattering at grazing incidence
+//
+//! @file      Fit/Parameters/AttLimits.h
+//! @brief     Defines and implements class AttLimits.
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2015
+//! @authors   Scientific Computing Group at MLZ Garching
+//! @authors   C. Durniak, M. Ganeva, G. Pospelov, W. Van Herck, J. Wuttke
+//
+// ************************************************************************** //
+
+#ifndef ATTLIMITS_H
+#define ATTLIMITS_H
+
+#include "WinDllMacros.h"
+#include "RealLimits.h"
+#include "Attributes.h"
+
+//! @class AttLimits
+//! @ingroup fitting
+//! @brief The AttLimits class defines limited/free attribute of fit parameter.
+
+class BA_CORE_API_ AttLimits
+{
+public:
+    AttLimits();
+
+    static AttLimits lowerLimited(double bound_value);
+    static AttLimits positive();
+    static AttLimits nonnegative();
+    static AttLimits upperLimited(double bound_value);
+    static AttLimits limited(double left_bound_value, double right_bound_value);
+    static AttLimits fixed();
+
+    bool isFixed() const;
+    bool isLimited() const;
+    bool isUpperLimited() const;
+    bool isLowerLimited() const;
+    bool isLimitless() const;
+
+    double lowerLimit() const;
+    double upperLimit() const;
+
+    void setFixed(bool isFixed);
+
+private:
+    AttLimits(const RealLimits &limits, const Attributes &fixedAttr);
+
+    RealLimits m_limits;
+    Attributes m_att_fixed;
+};
+
+
+#endif
diff --git a/Fit/Parameters/Attributes.h b/Fit/Parameters/Attributes.h
index d7554fa7c5fa8a23356416bcd6fbbcacc0b47ea0..63ed29bc79ce542b541589baf6932fc06d0a79ed 100644
--- a/Fit/Parameters/Attributes.h
+++ b/Fit/Parameters/Attributes.h
@@ -32,6 +32,7 @@ class BA_CORE_API_ Attributes
 
     void setFixed(bool is_fixed) { m_is_fixed = is_fixed; }
     bool isFixed() const { return m_is_fixed; }
+    bool isFree() const { return !isFixed(); }
 
     friend std::ostream& operator<<(std::ostream& ostr, const Attributes& m) {
         m.print(ostr); return ostr; }
diff --git a/Tests/UnitTests/Fit/0/AttLimitsTest.h b/Tests/UnitTests/Fit/0/AttLimitsTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..8b3f4d6c3a6d5f154396c22b7fb55930f2cb73fd
--- /dev/null
+++ b/Tests/UnitTests/Fit/0/AttLimitsTest.h
@@ -0,0 +1,83 @@
+#ifndef ATTLIMITSTEST_H
+#define ATTLIMITSTEST_H
+
+#include "AttLimits.h"
+#include "gtest/gtest.h"
+
+class AttLimitsTest : public ::testing::Test
+{
+ protected:
+    AttLimitsTest(){}
+    virtual ~AttLimitsTest(){}
+
+};
+
+TEST_F(AttLimitsTest, InitialState)
+{
+    AttLimits limits;
+    EXPECT_FALSE(limits.isFixed());
+    EXPECT_FALSE(limits.isLimited());
+    EXPECT_FALSE(limits.isUpperLimited());
+    EXPECT_FALSE(limits.isLowerLimited());
+    EXPECT_TRUE(limits.isLimitless());
+}
+
+TEST_F(AttLimitsTest, LowerLimited)
+{
+    AttLimits limits = AttLimits::lowerLimited(1.0);
+    EXPECT_FALSE(limits.isFixed());
+    EXPECT_FALSE(limits.isLimited());
+    EXPECT_FALSE(limits.isUpperLimited());
+    EXPECT_TRUE(limits.isLowerLimited());
+    EXPECT_FALSE(limits.isLimitless());
+    EXPECT_EQ(1.0, limits.lowerLimit());
+    EXPECT_EQ(0.0, limits.upperLimit());
+}
+
+TEST_F(AttLimitsTest, UpperLimited)
+{
+    AttLimits limits = AttLimits::upperLimited(1.0);
+    EXPECT_FALSE(limits.isFixed());
+    EXPECT_FALSE(limits.isLimited());
+    EXPECT_TRUE(limits.isUpperLimited());
+    EXPECT_FALSE(limits.isLowerLimited());
+    EXPECT_FALSE(limits.isLimitless());
+    EXPECT_EQ(0.0, limits.lowerLimit());
+    EXPECT_EQ(1.0, limits.upperLimit());
+}
+
+TEST_F(AttLimitsTest, Fixed)
+{
+    AttLimits limits = AttLimits::fixed();
+    EXPECT_TRUE(limits.isFixed());
+    EXPECT_FALSE(limits.isLimited());
+    EXPECT_FALSE(limits.isUpperLimited());
+    EXPECT_FALSE(limits.isLowerLimited());
+    EXPECT_FALSE(limits.isLimitless());
+    EXPECT_EQ(0.0, limits.lowerLimit());
+    EXPECT_EQ(0.0, limits.upperLimit());
+}
+
+TEST_F(AttLimitsTest, Limited)
+{
+    AttLimits limits = AttLimits::limited(1.0, 2.0);
+    EXPECT_FALSE(limits.isFixed());
+    EXPECT_TRUE(limits.isLimited());
+    EXPECT_FALSE(limits.isUpperLimited());
+    EXPECT_FALSE(limits.isLowerLimited());
+    EXPECT_FALSE(limits.isLimitless());
+    EXPECT_EQ(1.0, limits.lowerLimit());
+    EXPECT_EQ(2.0, limits.upperLimit());
+
+    // making it fixed, this should remove limits
+    limits.setFixed(true);
+    EXPECT_TRUE(limits.isFixed());
+    EXPECT_FALSE(limits.isLimited());
+    EXPECT_FALSE(limits.isUpperLimited());
+    EXPECT_FALSE(limits.isLowerLimited());
+    EXPECT_FALSE(limits.isLimitless());
+    EXPECT_EQ(0.0, limits.lowerLimit());
+    EXPECT_EQ(0.0, limits.upperLimit());
+}
+
+#endif
diff --git a/Tests/UnitTests/Fit/0/testlist.h b/Tests/UnitTests/Fit/0/testlist.h
index dd3a35e4cd13f9dcb0419832113335106c635e79..e640daa6ab6d391d697654d745abdef55ff16c0d 100644
--- a/Tests/UnitTests/Fit/0/testlist.h
+++ b/Tests/UnitTests/Fit/0/testlist.h
@@ -1,5 +1,6 @@
 // To renew this file, run /G/ba/dev-tools/code-tools/update-gtestlist.py <directory>
 
+#include "AttLimitsTest.h"
 #include "FitObjectTest.h"
 #include "FitParameterLinkedTest.h"
 #include "FitParameterSetTest.h"
diff --git a/auto/Wrap/libBornAgainFit.py b/auto/Wrap/libBornAgainFit.py
index ff3aa107c8a2be2a4d642dfbb773dd8d0311eea7..89330460ea915a2828aaa7900964bbf3178dbe0d 100644
--- a/auto/Wrap/libBornAgainFit.py
+++ b/auto/Wrap/libBornAgainFit.py
@@ -1502,6 +1502,11 @@ class Attributes(_object):
         return _libBornAgainFit.Attributes_isFixed(self)
 
 
+    def isFree(self):
+        """isFree(Attributes self) -> bool"""
+        return _libBornAgainFit.Attributes_isFree(self)
+
+
     def __eq__(self, other):
         """__eq__(Attributes self, Attributes other) -> bool"""
         return _libBornAgainFit.Attributes___eq__(self, other)
diff --git a/auto/Wrap/libBornAgainFit_wrap.cpp b/auto/Wrap/libBornAgainFit_wrap.cpp
index 198a83a398efcd0cc91b83d3cc6ce4fedcabc046..4a69cda343f483d8253e21b94c7946e6382010a5 100644
--- a/auto/Wrap/libBornAgainFit_wrap.cpp
+++ b/auto/Wrap/libBornAgainFit_wrap.cpp
@@ -18643,6 +18643,28 @@ fail:
 }
 
 
+SWIGINTERN PyObject *_wrap_Attributes_isFree(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  Attributes *arg1 = (Attributes *) 0 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  PyObject * obj0 = 0 ;
+  bool result;
+  
+  if (!PyArg_ParseTuple(args,(char *)"O:Attributes_isFree",&obj0)) SWIG_fail;
+  res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_Attributes, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Attributes_isFree" "', argument " "1"" of type '" "Attributes const *""'"); 
+  }
+  arg1 = reinterpret_cast< Attributes * >(argp1);
+  result = (bool)((Attributes const *)arg1)->isFree();
+  resultobj = SWIG_From_bool(static_cast< bool >(result));
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
 SWIGINTERN PyObject *_wrap_Attributes___eq__(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
   PyObject *resultobj = 0;
   Attributes *arg1 = (Attributes *) 0 ;
@@ -22182,6 +22204,7 @@ static PyMethodDef SwigMethods[] = {
 		"bool Attributes::isFixed() const \n"
 		"\n"
 		""},
+	 { (char *)"Attributes_isFree", _wrap_Attributes_isFree, METH_VARARGS, (char *)"Attributes_isFree(Attributes self) -> bool"},
 	 { (char *)"Attributes___eq__", _wrap_Attributes___eq__, METH_VARARGS, (char *)"Attributes___eq__(Attributes self, Attributes other) -> bool"},
 	 { (char *)"Attributes___ne__", _wrap_Attributes___ne__, METH_VARARGS, (char *)"Attributes___ne__(Attributes self, Attributes other) -> bool"},
 	 { (char *)"delete_Attributes", _wrap_delete_Attributes, METH_VARARGS, (char *)"delete_Attributes(Attributes self)"},