diff --git a/Base/Axis/Bin.cpp b/Base/Axis/Bin.cpp
index ce246ee5310d9de202b478f26b723af5c1099a95..bfaeafb5b3c62b05746c69a59e6dfea24c1430e4 100644
--- a/Base/Axis/Bin.cpp
+++ b/Base/Axis/Bin.cpp
@@ -42,7 +42,5 @@ std::optional<Bin1D> Bin1D::clipped_or_nil(double lower, double upper) const
     ASSERT(lower <= upper);
     if (upper < m_lower || m_upper < lower)
         return {};
-    if (lower <= m_lower && m_upper <= upper)
-        return *this;
     return Bin1D(std::max(lower, m_lower), std::min(upper, m_upper));
 }
diff --git a/Base/Axis/DiscreteAxis.cpp b/Base/Axis/DiscreteAxis.cpp
index 1917be99a70301e4d05c7e7fbad19f2fecbf1f9e..75d42a843a3f0a0fa0d52480524c4906c7f2c9c2 100644
--- a/Base/Axis/DiscreteAxis.cpp
+++ b/Base/Axis/DiscreteAxis.cpp
@@ -47,25 +47,6 @@ DiscreteAxis::DiscreteAxis(const std::string& name, size_t n, double first, doub
 {
 }
 
-DiscreteAxis* DiscreteAxis::clone() const
-{
-    return new DiscreteAxis(axisName(), m_coordinates);
-}
-
-void DiscreteAxis::clip(double lower, double upper)
-{
-    if (lower >= upper)
-        throw std::runtime_error("DiscreteAxis::clip() called with invalid lower >= upper");
-
-    using diff_t = std::vector<double>::iterator::difference_type;
-    const auto begin = m_coordinates.begin() + static_cast<diff_t>(closestIndex(lower));
-    const auto end = m_coordinates.begin() + static_cast<diff_t>(closestIndex(upper)) + 1;
-
-    m_coordinates = std::vector<double>(begin, end);
-    sanityCheck();
-    m_bins = centers2bins(m_coordinates);
-}
-
 void DiscreteAxis::checkIndex(size_t index) const
 {
     if (m_coordinates.size() > index)
diff --git a/Base/Axis/DiscreteAxis.h b/Base/Axis/DiscreteAxis.h
index 13352637aedee428ced19e0560b30650950bf20c..896fbfbe5c867c38c56e09536ec2aa7fd0d8fdfb 100644
--- a/Base/Axis/DiscreteAxis.h
+++ b/Base/Axis/DiscreteAxis.h
@@ -24,10 +24,6 @@ public:
     DiscreteAxis(const std::string& name, const std::vector<double>& points);
     DiscreteAxis(const std::string& name, size_t n, double first, double last);
 
-    DiscreteAxis* clone() const override;
-
-    void clip(double lower, double upper) override;
-
 private:
     void checkIndex(size_t index) const;
     void sanityCheck() const;
diff --git a/Base/Axis/FixedBinAxis.cpp b/Base/Axis/FixedBinAxis.cpp
index 6080c5b1175240e5ad9e4c2da62a7d6b1a1dc0d8..7736383673ff2ac550be5b2ef833cf29ea94ef79 100644
--- a/Base/Axis/FixedBinAxis.cpp
+++ b/Base/Axis/FixedBinAxis.cpp
@@ -42,38 +42,6 @@ FixedBinAxis::FixedBinAxis(const std::string& name, size_t nbins, double start,
 {
 }
 
-FixedBinAxis* FixedBinAxis::clone() const
-{
-    auto* result = new FixedBinAxis(axisName(), m_nbins, m_start, m_end);
-    return result;
-}
-
-void FixedBinAxis::clip(double lower, double upper)
-{
-    if (lower >= upper)
-        throw std::runtime_error(
-            "FixedBinAxis::clip -> Error. 'lower' should be smaller than 'upper'");
-
-    if (lower < min())
-        lower = bin(0).center();
-    if (upper >= max())
-        upper = bin(size() - 1).center();
-
-    const size_t nbin1 = closestIndex(lower);
-    const size_t nbin2 = closestIndex(upper);
-
-    // create tmp vars until everything is calculated, otherwise the calculation will be corrupted
-    // by partially changed values
-    const auto newStart = bin(nbin1).lowerBound();
-    const auto newEnd = bin(nbin2).upperBound();
-
-    m_nbins = nbin2 - nbin1 + 1;
-    m_start = newStart;
-    m_end = newEnd;
-
-    m_bins = bounds2bins(m_nbins, m_start, m_end);
-}
-
 //... In global namespace.
 
 FixedBinAxis FixedScanAxis(const std::string& name, size_t nbins, double scan_start,
diff --git a/Base/Axis/FixedBinAxis.h b/Base/Axis/FixedBinAxis.h
index f28b148c30fd69909aecf59c15be57ea029ac2fd..07a78a2877f206d1edaf699c9d357bfdae82f715 100644
--- a/Base/Axis/FixedBinAxis.h
+++ b/Base/Axis/FixedBinAxis.h
@@ -27,12 +27,6 @@ public:
     //! @param start low edge of first bin
     //! @param end upper edge of last bin
     FixedBinAxis(const std::string& name, size_t nbins, double start, double end);
-    ~FixedBinAxis() override = default;
-    FixedBinAxis(FixedBinAxis&&) = default;
-
-    FixedBinAxis* clone() const override;
-
-    void clip(double lower, double upper) override;
 
 private:
     size_t m_nbins;
diff --git a/Base/Axis/IAxis.cpp b/Base/Axis/IAxis.cpp
index 34d2b142a58e94164b1ce87ccf92f92538e9d273..c9d63028d02f74beb03662fe7d9b8008502b4986 100644
--- a/Base/Axis/IAxis.cpp
+++ b/Base/Axis/IAxis.cpp
@@ -16,9 +16,6 @@
 #include "Base/Util/Algorithms.h"
 #include "Base/Util/Assert.h"
 #include <iomanip>
-#include <optional>
-
-IAxis::~IAxis() = default;
 
 IAxis::IAxis(std::string name, const std::vector<Bin1D>& bins)
     : m_name(name)
@@ -87,19 +84,43 @@ size_t IAxis::closestIndex(double value) const
     return size() - 1;
 }
 
-bool IAxis::operator==(const IAxis& other) const
+bool IAxis::isEquidistantCoverage() const
 {
-    return m_name == other.m_name && m_bins == other.m_bins;
+    const size_t N = size();
+    for (size_t i = 0; i < N; ++i) {
+        const Bin1D& b = bin(i);
+        if (b.lowerBound() != (N - i) * (min() / N) + i * (max() / N)
+            || b.upperBound() != (N - i - 1) * (min() / N) + (i + 1) * (max() / N))
+            return false;
+    }
+    return true;
 }
 
-void IAxis::clip(double /*lower*/, double /*upper*/)
+bool IAxis::isDiscrete() const
 {
-    ASSERT(false); // not implemented
+    for (const Bin1D& b : bins())
+        if (b.binSize())
+            return false;
+    return true;
+}
+
+IAxis IAxis::clipped(double lower, double upper) const
+{
+    std::vector<Bin1D> out_bins;
+    for (const Bin1D& b : m_bins)
+        if (auto bc = b.clipped_or_nil(lower, upper))
+            out_bins.emplace_back(bc.value());
+    return {m_name, out_bins};
 }
 
-void IAxis::clip(const std::pair<double, double> bounds)
+IAxis IAxis::clipped(std::pair<double, double> bounds) const
 {
-    return clip(bounds.first, bounds.second);
+    return clipped(bounds.first, bounds.second);
+}
+
+bool IAxis::operator==(const IAxis& other) const
+{
+    return m_name == other.m_name && m_bins == other.m_bins;
 }
 
 std::ostream& operator<<(std::ostream& ostr, const IAxis& ax)
@@ -108,30 +129,14 @@ std::ostream& operator<<(std::ostream& ostr, const IAxis& ax)
     ASSERT(N > 0);
 
     // Special output for FixedBinAxis:
-    bool isFixedBinAxis = true;
-    for (size_t i = 0; i < N; ++i) {
-        const Bin1D& b = ax.bin(i);
-        if (b.lowerBound() != (N - i) * (ax.min() / N) + i * (ax.max() / N)
-            || b.upperBound() != (N - i - 1) * (ax.min() / N) + (i + 1) * (ax.max() / N)) {
-            isFixedBinAxis = false;
-            break;
-        }
-    }
-    if (isFixedBinAxis) {
+    if (ax.isEquidistantCoverage()) {
         ostr << "FixedBinAxis(\"" << ax.axisName() << "\", " << ax.size() << ", "
              << std::setprecision(17) << ax.min() << ", " << ax.max() << ")";
         return ostr;
     }
 
     // Special output for DiscreteAxis
-    bool isDiscreteAxis = true;
-    for (const Bin1D& b : ax.bins()) {
-        if (b.binSize()) {
-            isDiscreteAxis = false;
-            break;
-        }
-    }
-    if (isDiscreteAxis) {
+    if (ax.isDiscrete()) {
         ostr << "DiscreteAxis(\"" << ax.axisName() << "\", "
              << ", [" << std::setprecision(17);
         for (double v : ax.binCenters())
diff --git a/Base/Axis/IAxis.h b/Base/Axis/IAxis.h
index a1a42f4f3a10370adc89ffa6b583d58e42947ee3..68b734e0ade6234e3800d9f03b895f1c97e1ab18 100644
--- a/Base/Axis/IAxis.h
+++ b/Base/Axis/IAxis.h
@@ -25,11 +25,7 @@
 class IAxis {
 public:
     IAxis(std::string name, const std::vector<Bin1D>& bins);
-    IAxis(const IAxis&) = delete;
-    IAxis(IAxis&&) = default;
-    virtual ~IAxis();
-
-    virtual IAxis* clone() const = 0;
+    IAxis* clone() const { return new IAxis(*this); }
 
     //! Sets the axis label
     void setAxisName(std::string name) { m_name = name; }
@@ -70,12 +66,11 @@ public:
     //! find bin index which is best match for given value
     size_t closestIndex(double value) const;
 
-    //! Clips this axis to the given values
-    virtual void clip(double lower, double upper);
+    bool isEquidistantCoverage() const;
+    bool isDiscrete() const;
 
-    //! Convenience overload to clip this axis to the given values.
-    //! bounds.first is lower, bounds.second is upper value.
-    void clip(std::pair<double, double> bounds);
+    IAxis clipped(double lower, double upper) const;
+    IAxis clipped(std::pair<double, double> bounds) const;
 
     bool operator==(const IAxis& right) const;
 
diff --git a/Device/Data/Datafield.cpp b/Device/Data/Datafield.cpp
index aaa825cf9acb10b623aa2eb6969de35d26922110..e51d32c2f0b2f3c99ca59d60a11075857d3cad82 100644
--- a/Device/Data/Datafield.cpp
+++ b/Device/Data/Datafield.cpp
@@ -176,17 +176,15 @@ double Datafield::minVal() const
 
 Datafield* Datafield::crop(double xmin, double ymin, double xmax, double ymax) const
 {
-    const std::unique_ptr<IAxis> xaxis{xAxis().clone()};
-    const std::unique_ptr<IAxis> yaxis{yAxis().clone()};
-    xaxis->clip(xmin, xmax);
-    yaxis->clip(ymin, ymax);
+    const auto xclipped = std::make_unique<IAxis>(xAxis().clipped(xmin, xmax));
+    const auto yclipped = std::make_unique<IAxis>(yAxis().clipped(ymin, ymax));
 
     std::vector<double> out(size());
     size_t iout = 0;
     for (size_t i = 0; i < size(); ++i) {
         double x = frame().projectedCoord(i, 0);
         double y = frame().projectedCoord(i, 1);
-        if (xaxis->rangeComprises(x) && yaxis->rangeComprises(y))
+        if (xclipped->rangeComprises(x) && yclipped->rangeComprises(y))
             out[iout++] = m_values[i];
     }
     return new Datafield(frame().clone(), out);
@@ -194,13 +192,13 @@ Datafield* Datafield::crop(double xmin, double ymin, double xmax, double ymax) c
 
 Datafield* Datafield::crop(double xmin, double xmax) const
 {
-    const std::unique_ptr<IAxis> xaxis{xAxis().clone()};
-    xaxis->clip(xmin, xmax);
+    const auto xclipped = std::make_unique<IAxis>(xAxis().clipped(xmin, xmax));
+
     std::vector<double> out(size());
     size_t iout = 0;
     for (size_t i = 0; i < size(); ++i) {
         const double x = frame().projectedCoord(i, 0);
-        if (xaxis->rangeComprises(x))
+        if (xclipped->rangeComprises(x))
             out[iout++] = m_values[i];
     }
     return new Datafield(frame().clone(), out);
diff --git a/Device/Detector/IDetector.cpp b/Device/Detector/IDetector.cpp
index 82393eebca94bc82962f91867a578c65a7194dd7..76bfdcd0c81a629ddf2e44a65ef3299a7ee05bc2 100644
--- a/Device/Detector/IDetector.cpp
+++ b/Device/Detector/IDetector.cpp
@@ -96,8 +96,8 @@ size_t IDetector::sizeOfExplicitRegionOfInterest() const
 size_t IDetector::totalSize() const
 {
     size_t result = 1;
-    for (size_t i_axis = 0; i_axis < 2; ++i_axis)
-        result *= m_axes[i_axis]->size();
+    for (size_t i = 0; i < 2; ++i)
+        result *= m_axes[i]->size();
     return result;
 }
 
@@ -115,11 +115,8 @@ bool IDetector::hasExplicitRegionOfInterest() const
 std::vector<const IAxis*> IDetector::axesClippedToRegionOfInterest() const
 {
     std::vector<const IAxis*> result;
-    for (size_t iAxis = 0; iAxis < m_axes.size(); ++iAxis) {
-        auto* axis = m_axes[iAxis]->clone();
-        axis->clip(regionOfInterestBounds(iAxis));
-        result.emplace_back(axis);
-    }
+    for (size_t i = 0; i < m_axes.size(); ++i)
+        result.emplace_back(new IAxis(m_axes[i]->clipped(regionOfInterestBounds(i))));
     return result;
 }
 
@@ -165,11 +162,8 @@ void IDetector::applyDetectorResolution(Datafield* intensity_map) const
 Datafield IDetector::createDetectorMap() const
 {
     std::vector<const IAxis*> axes;
-    for (size_t iAxis = 0; iAxis < 2; ++iAxis) {
-        IAxis* tmp = axis(iAxis).clone();
-        tmp->clip(regionOfInterestBounds(iAxis));
-        axes.emplace_back(tmp);
-    }
+    for (size_t i = 0; i < 2; ++i)
+        axes.emplace_back(new IAxis(axis(i).clipped(regionOfInterestBounds(i))));
     return Datafield(std::move(axes));
 }
 
diff --git a/Sim/Export/PyFmt2.cpp b/Sim/Export/PyFmt2.cpp
index 91cd1fdee0357001a15188fff1226530037e6aef..a9a036e3e7ae6fa08b2fde69c4c8a2fcf7051bb0 100644
--- a/Sim/Export/PyFmt2.cpp
+++ b/Sim/Export/PyFmt2.cpp
@@ -16,8 +16,6 @@
 #include "Base/Axis/DiscreteAxis.h"
 #include "Base/Axis/FixedBinAxis.h"
 #include "Base/Const/Units.h"
-#include <numbers>
-using std::numbers::pi;
 #include "Base/Py/PyFmt.h"
 #include "Base/Util/Algorithms.h"
 #include "Base/Util/Assert.h"
@@ -30,6 +28,9 @@ using std::numbers::pi;
 #include "Param/Distrib/Distributions.h"
 #include "Param/Distrib/ParameterDistribution.h"
 #include <iomanip>
+#include <numbers>
+
+using std::numbers::pi;
 
 //! Returns fixed Python code snippet that defines the function "simulate".
 
@@ -89,14 +90,14 @@ std::string Py::Fmt2::representShape2D(const std::string& indent, const IShape2D
 }
 
 //! Prints an axis.
-std::string Py::Fmt2::printAxis(const IAxis* axis, const std::string& unit)
+std::string Py::Fmt2::printAxis(const IAxis* a, const std::string& unit)
 {
     std::ostringstream result;
-    if (const auto* a = dynamic_cast<const FixedBinAxis*>(axis); a)
+    if (a->isEquidistantCoverage())
         result << "ba.FixedBinAxis(" << Py::Fmt::printString(a->axisName()) << ", " << a->size()
                << ", " << Py::Fmt::printValue(a->min(), unit) << ", "
                << Py::Fmt::printValue(a->max(), unit) << ")";
-    else if (const auto* a = dynamic_cast<const DiscreteAxis*>(axis); a) {
+    else if (a->isDiscrete()) {
         result << "numpy.asarray([";
         const std::vector<double>& points = a->binCenters();
         for (auto iter = points.begin(); iter != points.end() - 1; ++iter)
diff --git a/Tests/Unit/Device/DepthprobeConverterTest.cpp b/Tests/Unit/Device/DepthprobeConverterTest.cpp
index 641e0056702a473285c5ca509872a5b8cea5377f..c70da85ff9575b1062c8519fd2cd82b69c925e1e 100644
--- a/Tests/Unit/Device/DepthprobeConverterTest.cpp
+++ b/Tests/Unit/Device/DepthprobeConverterTest.cpp
@@ -65,7 +65,6 @@ void DepthprobeCoordsTest::checkMainFunctionality(const DepthprobeCoords& test_o
 void DepthprobeCoordsTest::checkAlphaAxis(Coords units, const DepthprobeCoords& test_object)
 {
     std::unique_ptr<IAxis> axis(test_object.convertedAxis(0, units));
-    EXPECT_TRUE(dynamic_cast<const FixedBinAxis*>(axis.get()));
     EXPECT_EQ(axis->size(), m_nbins);
     EXPECT_EQ(axis->min(), test_object.calculateMin(0, units));
     EXPECT_EQ(axis->max(), test_object.calculateMax(0, units));
@@ -74,7 +73,6 @@ void DepthprobeCoordsTest::checkAlphaAxis(Coords units, const DepthprobeCoords&
 void DepthprobeCoordsTest::checkZAxis(Coords units, const DepthprobeCoords& test_object)
 {
     std::unique_ptr<IAxis> axis(test_object.convertedAxis(1, units));
-    EXPECT_TRUE(dynamic_cast<const FixedBinAxis*>(axis.get()));
     EXPECT_EQ(axis->size(), m_nbins);
 
     EXPECT_EQ(axis->min(), test_object.calculateMin(1, units));
diff --git a/Tests/Unit/Device/FixedBinAxisTest.cpp b/Tests/Unit/Device/FixedBinAxisTest.cpp
index 7be90ef4803686757258240ae3e60552667f5e4d..a75412089196ebbb0921dc3cad4e6be2158c4af4 100644
--- a/Tests/Unit/Device/FixedBinAxisTest.cpp
+++ b/Tests/Unit/Device/FixedBinAxisTest.cpp
@@ -104,7 +104,7 @@ TEST(FixedBinAxisTest, CheckEquality)
 TEST(FixedBinAxisTest, CheckClone)
 {
     FixedBinAxis a1("axis", 99, -1.2, 5.4);
-    FixedBinAxis* clone = a1.clone();
+    IAxis* clone = a1.clone();
     EXPECT_TRUE(a1 == *clone);
     delete clone;
 }
@@ -134,24 +134,3 @@ TEST(FixedBinAxisTest, BinCenters)
     EXPECT_DOUBLE_EQ(axis.binCenter(1), centers[1]);
     EXPECT_DOUBLE_EQ(axis.binCenter(2), centers[2]);
 }
-
-TEST(FixedBinAxisTest, ClippedAxis)
-{
-    FixedBinAxis axis("name", 4, -1.0, 3.0);
-
-    FixedBinAxis* clip1 = axis.clone();
-    clip1->clip(-0.5, 2.5);
-    EXPECT_EQ(clip1->size(), axis.size());
-    EXPECT_EQ(clip1->min(), axis.min());
-    EXPECT_EQ(clip1->max(), axis.max());
-    EXPECT_TRUE(*clip1 == axis);
-    delete clip1;
-
-    FixedBinAxis* clip2 = axis.clone();
-    clip2->clip(0.0, 1.99);
-    EXPECT_EQ(clip2->size(), size_t(2));
-    EXPECT_EQ(clip2->min(), 0.0);
-    EXPECT_EQ(clip2->max(), 2.0);
-    EXPECT_TRUE(*clip2 != axis);
-    delete clip2;
-}
diff --git a/Tests/Unit/Device/OffspecConverterTest.cpp b/Tests/Unit/Device/OffspecConverterTest.cpp
index 2e74ad8a75c58732df1925f656064815c5a33639..e186f63ae13096e72ccb39cbb60903bd9a1dbd42 100644
--- a/Tests/Unit/Device/OffspecConverterTest.cpp
+++ b/Tests/Unit/Device/OffspecConverterTest.cpp
@@ -67,12 +67,10 @@ TEST_F(OffspecCoordsTest, OffspecCoords)
     EXPECT_FAILED_ASSERT(coords->calculateMax(2, Coords::UNDEFINED));
 
     std::unique_ptr<IAxis> axis(coords->convertedAxis(0, Coords::UNDEFINED));
-    EXPECT_TRUE(dynamic_cast<const FixedBinAxis*>(axis.get()));
     EXPECT_EQ(axis->min(), coords->calculateMin(0, Coords::UNDEFINED));
     EXPECT_EQ(axis->max(), coords->calculateMax(0, Coords::UNDEFINED));
 
     std::unique_ptr<IAxis> axis2(coords->convertedAxis(1, Coords::RADIANS));
-    EXPECT_TRUE(dynamic_cast<const FixedBinAxis*>(axis2.get()));
     EXPECT_EQ(axis2->min(), coords->calculateMin(1, Coords::RADIANS));
     EXPECT_EQ(axis2->max(), coords->calculateMax(1, Coords::RADIANS));
 
diff --git a/Tests/Unit/Device/PointwiseAxisTest.cpp b/Tests/Unit/Device/PointwiseAxisTest.cpp
index 9f7163afad4609101e95ac7af93c57f233789bbc..2e0975330c98cdbe93dbcbf520f81f5327238032 100644
--- a/Tests/Unit/Device/PointwiseAxisTest.cpp
+++ b/Tests/Unit/Device/PointwiseAxisTest.cpp
@@ -75,7 +75,7 @@ TEST(PointwiseAxisTest, CheckEquality)
 TEST(PointwiseAxisTest, CheckClone)
 {
     DiscreteAxis a1("axis", std::vector<double>{1.0, 2.0, 5.0});
-    std::unique_ptr<DiscreteAxis> clone(a1.clone());
+    IAxis* clone = a1.clone();
     EXPECT_TRUE(a1 == *clone);
 }
 
@@ -95,49 +95,20 @@ TEST(PointwiseAxisTest, ClippedAxis)
 {
     DiscreteAxis axis("name", std::vector<double>{1.0, 2.0, 2.5, 2.7, 5.0});
 
-    std::unique_ptr<DiscreteAxis> clip1(axis.clone());
-    clip1->clip(0.99, 5.1);
-    EXPECT_TRUE(*clip1 == axis);
+    IAxis clip1 = axis.clipped(0.99, 5.1);
+    EXPECT_TRUE(clip1 == axis);
 
-    std::unique_ptr<DiscreteAxis> clip2(axis.clone());
-    clip2->clip(1, 5.0);
-    EXPECT_TRUE(*clip2 == axis);
+    IAxis clip2 = axis.clipped(1, 5.0);
+    EXPECT_TRUE(clip2 == axis);
 
-    std::unique_ptr<DiscreteAxis> clip3(axis.clone());
-    clip3->clip(1.5, 2.5);
-    EXPECT_TRUE(*clip3 != axis);
-    EXPECT_EQ(clip3->size(), 2u);
-    EXPECT_EQ(clip3->binCenter(0), 2.0);
-    EXPECT_EQ(clip3->binCenter(1), 2.5);
+    IAxis clip3 = axis.clipped(1.5, 2.5);
+    EXPECT_TRUE(clip3 != axis);
+    EXPECT_EQ(clip3.size(), 2u);
+    EXPECT_EQ(clip3.binCenter(0), 2.0);
+    EXPECT_EQ(clip3.binCenter(1), 2.5);
 
     // TODO axis restore EXPECT_THROW(axis.clone()->clip(1.4, 1.6), std::runtime_error);
-    EXPECT_THROW(axis.clone()->clip(5.0, 1.0), std::runtime_error);
-}
-
-TEST(PointwiseAxisTest, FixedBinAxisComparison)
-{
-    FixedBinAxis fixed_axis("name", 4, 0.0, 4.0);
-    DiscreteAxis pointwise_axis("name", std::vector<double>{0.5, 1.5, 2.5, 3.5});
-
-    EXPECT_EQ(fixed_axis.size(), pointwise_axis.size());
-    EXPECT_DOUBLE_EQ(fixed_axis.binCenter(0), pointwise_axis.binCenter(0));
-    EXPECT_DOUBLE_EQ(fixed_axis.binCenter(1), pointwise_axis.binCenter(1));
-    EXPECT_DOUBLE_EQ(fixed_axis.binCenter(2), pointwise_axis.binCenter(2));
-    EXPECT_DOUBLE_EQ(fixed_axis.binCenter(3), pointwise_axis.binCenter(3));
-
-    EXPECT_EQ(fixed_axis.closestIndex(0.0), pointwise_axis.closestIndex(0.0));
-    EXPECT_EQ(fixed_axis.closestIndex(0.99), pointwise_axis.closestIndex(0.99));
-    EXPECT_EQ(fixed_axis.closestIndex(1.0), pointwise_axis.closestIndex(1.0));
-    EXPECT_EQ(fixed_axis.closestIndex(2.5), pointwise_axis.closestIndex(2.5));
-    EXPECT_EQ(fixed_axis.closestIndex(4.5), pointwise_axis.closestIndex(4.5));
-
-    std::unique_ptr<FixedBinAxis> clipped_fixed(fixed_axis.clone());
-    clipped_fixed->clip(0.5, 3.5);
-    std::unique_ptr<DiscreteAxis> clipped_pointwise(pointwise_axis.clone());
-    clipped_pointwise->clip(0.5, 3.5);
-    EXPECT_EQ(clipped_fixed->size(), clipped_pointwise->size());
-    EXPECT_DOUBLE_EQ(clipped_fixed->binCenter(0), clipped_pointwise->binCenter(0));
-    EXPECT_DOUBLE_EQ(clipped_fixed->binCenter(3), clipped_pointwise->binCenter(3));
+    EXPECT_THROW(axis.clipped(5.0, 1.0), std::runtime_error);
 }
 
 TEST(PointwiseAxisTest, FixedBinAxisComparisonWithMask)
diff --git a/Tests/Unit/Device/RectangularConverterTest.cpp b/Tests/Unit/Device/RectangularConverterTest.cpp
index 44fd6d98fbf1b9364ae9d1ce5e24c05bf1c51725..89e5f576cfe59ee81525f927be1e555587e73a8c 100644
--- a/Tests/Unit/Device/RectangularConverterTest.cpp
+++ b/Tests/Unit/Device/RectangularConverterTest.cpp
@@ -90,12 +90,10 @@ TEST_F(RectangularConverterTest, ImageCoords)
     EXPECT_FAILED_ASSERT(converter.calculateMax(2, Coords::UNDEFINED));
 
     std::unique_ptr<IAxis> axis(converter.convertedAxis(0, Coords::UNDEFINED));
-    EXPECT_TRUE(dynamic_cast<const FixedBinAxis*>(axis.get()));
     EXPECT_EQ(axis->min(), converter.calculateMin(0, Coords::UNDEFINED));
     EXPECT_EQ(axis->max(), converter.calculateMax(0, Coords::UNDEFINED));
 
     std::unique_ptr<IAxis> axis2(converter.convertedAxis(1, Coords::QSPACE));
-    EXPECT_TRUE(dynamic_cast<const FixedBinAxis*>(axis2.get()));
     EXPECT_EQ(axis2->min(), converter.calculateMin(1, Coords::QSPACE));
     EXPECT_EQ(axis2->max(), converter.calculateMax(1, Coords::QSPACE));
 
diff --git a/Tests/Unit/Device/SphericalConverterTest.cpp b/Tests/Unit/Device/SphericalConverterTest.cpp
index 8d95bd6c268a9ce0aa0defc6e81593c302547a1f..80eddd2cee912c2e0eafce2753ac882dd1d49edc 100644
--- a/Tests/Unit/Device/SphericalConverterTest.cpp
+++ b/Tests/Unit/Device/SphericalConverterTest.cpp
@@ -64,12 +64,10 @@ TEST_F(SphericalConverterTest, SphericalCoords)
     EXPECT_FAILED_ASSERT(converter.calculateMax(2, Coords::UNDEFINED));
 
     std::unique_ptr<IAxis> axis(converter.convertedAxis(0, Coords::UNDEFINED));
-    EXPECT_TRUE(dynamic_cast<const FixedBinAxis*>(axis.get()));
     EXPECT_EQ(axis->min(), converter.calculateMin(0, Coords::UNDEFINED));
     EXPECT_EQ(axis->max(), converter.calculateMax(0, Coords::UNDEFINED));
 
     std::unique_ptr<IAxis> axis2(converter.convertedAxis(1, Coords::QSPACE));
-    EXPECT_TRUE(dynamic_cast<const FixedBinAxis*>(axis2.get()));
     EXPECT_EQ(axis2->min(), converter.calculateMin(1, Coords::QSPACE));
     EXPECT_EQ(axis2->max(), converter.calculateMax(1, Coords::QSPACE));
 
diff --git a/Tests/Unit/Device/SphericalDetectorTest.cpp b/Tests/Unit/Device/SphericalDetectorTest.cpp
index 0cca5f28322880ada4f05680e4e1d224a2f9f6ef..e02eec19f7b1071996a08b58a363dc77cb812e0f 100644
--- a/Tests/Unit/Device/SphericalDetectorTest.cpp
+++ b/Tests/Unit/Device/SphericalDetectorTest.cpp
@@ -119,11 +119,11 @@ TEST(SphericalDetectorTest, Clone)
 
     auto data = clone->createDetectorMap();
     EXPECT_EQ(data.axis(0).size(), 4u);
-    EXPECT_EQ(data.axis(0).min(), 0.0 * Units::deg);
-    EXPECT_EQ(data.axis(0).max(), 4.0 * Units::deg);
+    EXPECT_EQ(data.axis(0).min(), 0.1 * Units::deg);
+    EXPECT_EQ(data.axis(0).max(), 3.0 * Units::deg);
     EXPECT_EQ(data.axis(1).size(), 2u);
-    EXPECT_EQ(data.axis(1).min(), 1.0 * Units::deg);
-    EXPECT_EQ(data.axis(1).max(), 3.0 * Units::deg);
+    EXPECT_EQ(data.axis(1).min(), 1.1 * Units::deg);
+    EXPECT_EQ(data.axis(1).max(), 2.9 * Units::deg);
 
     EXPECT_EQ(clone->detectorMask()->numberOfMaskedChannels(), 8);
 
diff --git a/Tests/Unit/Sim/CoordSystem1DTest.cpp b/Tests/Unit/Sim/CoordSystem1DTest.cpp
index 60b84a063dd4b38fc70f833fb6b9233c0018478d..85c83d94100db29af5c754e0603ef4d7b4c4c707 100644
--- a/Tests/Unit/Sim/CoordSystem1DTest.cpp
+++ b/Tests/Unit/Sim/CoordSystem1DTest.cpp
@@ -46,20 +46,16 @@ void CoordSystem1DTest::checkConventionalConverter(const CoordSystem1D& test_obj
 
     // DEFAULT
     std::unique_ptr<IAxis> axis_default(test_object.convertedAxis(0, Coords::UNDEFINED));
-    const IAxis* tmp = axis_default.get();
-    EXPECT_TRUE(dynamic_cast<const DiscreteAxis*>(tmp));
     EXPECT_EQ(axis_default->min(), test_object.calculateMin(0, Coords::UNDEFINED));
     EXPECT_EQ(axis_default->max(), test_object.calculateMax(0, Coords::UNDEFINED));
 
     // QSPACE
     std::unique_ptr<const IAxis> axis_qspace(test_object.convertedAxis(0, Coords::QSPACE));
-    EXPECT_TRUE(dynamic_cast<const DiscreteAxis*>(axis_qspace.get()));
     EXPECT_EQ(axis_qspace->min(), test_object.calculateMin(0, Coords::QSPACE));
     EXPECT_EQ(axis_qspace->max(), test_object.calculateMax(0, Coords::QSPACE));
 
     // NBINS
     std::unique_ptr<IAxis> axis_nbins(test_object.convertedAxis(0, Coords::NBINS));
-    EXPECT_TRUE(dynamic_cast<const FixedBinAxis*>(axis_nbins.get()));
     EXPECT_EQ(axis_nbins->min(), test_object.calculateMin(0, Coords::NBINS));
     EXPECT_EQ(axis_nbins->max(), test_object.calculateMax(0, Coords::NBINS));
 }
@@ -79,20 +75,17 @@ void CoordSystem1DTest::checkQSpecConverter(const CoordSystem1D& test_object)
 
     // DEFAULT
     std::unique_ptr<IAxis> axis_default(test_object.convertedAxis(0, Coords::UNDEFINED));
-    EXPECT_TRUE(dynamic_cast<const DiscreteAxis*>(axis_default.get()));
     EXPECT_EQ(axis_default->min(), test_object.calculateMin(0, Coords::UNDEFINED));
     EXPECT_EQ(axis_default->max(), test_object.calculateMax(0, Coords::UNDEFINED));
 
     // QSPACE
     std::unique_ptr<IAxis> axis_qspace(test_object.convertedAxis(0, Coords::QSPACE));
-    EXPECT_TRUE(dynamic_cast<const DiscreteAxis*>(axis_qspace.get()));
     EXPECT_EQ(axis_qspace->min(), test_object.calculateMin(0, Coords::QSPACE));
     EXPECT_EQ(axis_qspace->max(), test_object.calculateMax(0, Coords::QSPACE));
     EXPECT_EQ(*axis_default, *axis_qspace);
 
     // NBINS
     std::unique_ptr<IAxis> axis_nbins(test_object.convertedAxis(0, Coords::NBINS));
-    EXPECT_TRUE(dynamic_cast<const FixedBinAxis*>(axis_nbins.get()));
     EXPECT_EQ(axis_nbins->min(), test_object.calculateMin(0, Coords::NBINS));
     EXPECT_EQ(axis_nbins->max(), test_object.calculateMax(0, Coords::NBINS));
 }
diff --git a/auto/Wrap/libBornAgainBase.py b/auto/Wrap/libBornAgainBase.py
index e3a1ce38242206eeccffe8078a9e4fa861158da6..8a8366969677a1ab71710bff889fc38f68bd5093 100644
--- a/auto/Wrap/libBornAgainBase.py
+++ b/auto/Wrap/libBornAgainBase.py
@@ -1848,11 +1848,11 @@ class IAxis(object):
     r"""Proxy of C++ IAxis class."""
 
     thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
-
-    def __init__(self, *args, **kwargs):
-        raise AttributeError("No constructor defined - class is abstract")
     __repr__ = _swig_repr
-    __swig_destroy__ = _libBornAgainBase.delete_IAxis
+
+    def __init__(self, name, bins):
+        r"""__init__(IAxis self, std::string name, std::vector< Bin1D,std::allocator< Bin1D > > const & bins) -> IAxis"""
+        _libBornAgainBase.IAxis_swiginit(self, _libBornAgainBase.new_IAxis(name, bins))
 
     def clone(self):
         r"""clone(IAxis self) -> IAxis"""
@@ -1914,16 +1914,25 @@ class IAxis(object):
         r"""closestIndex(IAxis self, double value) -> size_t"""
         return _libBornAgainBase.IAxis_closestIndex(self, value)
 
-    def clip(self, *args):
+    def isEquidistantCoverage(self):
+        r"""isEquidistantCoverage(IAxis self) -> bool"""
+        return _libBornAgainBase.IAxis_isEquidistantCoverage(self)
+
+    def isDiscrete(self):
+        r"""isDiscrete(IAxis self) -> bool"""
+        return _libBornAgainBase.IAxis_isDiscrete(self)
+
+    def clipped(self, *args):
         r"""
-        clip(IAxis self, double lower, double upper)
-        clip(IAxis self, pvacuum_double_t bounds)
+        clipped(IAxis self, double lower, double upper) -> IAxis
+        clipped(IAxis self, pvacuum_double_t bounds) -> IAxis
         """
-        return _libBornAgainBase.IAxis_clip(self, *args)
+        return _libBornAgainBase.IAxis_clipped(self, *args)
 
     def __eq__(self, right):
         r"""__eq__(IAxis self, IAxis right) -> bool"""
         return _libBornAgainBase.IAxis___eq__(self, right)
+    __swig_destroy__ = _libBornAgainBase.delete_IAxis
 
 # Register IAxis in _libBornAgainBase:
 _libBornAgainBase.IAxis_swigregister(IAxis)
@@ -1932,22 +1941,11 @@ class FixedBinAxis(IAxis):
 
     thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
     __repr__ = _swig_repr
-    __swig_destroy__ = _libBornAgainBase.delete_FixedBinAxis
 
-    def __init__(self, *args):
-        r"""
-        __init__(FixedBinAxis self, std::string const & name, size_t nbins, double start, double end) -> FixedBinAxis
-        __init__(FixedBinAxis self, FixedBinAxis arg2) -> FixedBinAxis
-        """
-        _libBornAgainBase.FixedBinAxis_swiginit(self, _libBornAgainBase.new_FixedBinAxis(*args))
-
-    def clone(self):
-        r"""clone(FixedBinAxis self) -> FixedBinAxis"""
-        return _libBornAgainBase.FixedBinAxis_clone(self)
-
-    def clip(self, lower, upper):
-        r"""clip(FixedBinAxis self, double lower, double upper)"""
-        return _libBornAgainBase.FixedBinAxis_clip(self, lower, upper)
+    def __init__(self, name, nbins, start, end):
+        r"""__init__(FixedBinAxis self, std::string const & name, size_t nbins, double start, double end) -> FixedBinAxis"""
+        _libBornAgainBase.FixedBinAxis_swiginit(self, _libBornAgainBase.new_FixedBinAxis(name, nbins, start, end))
+    __swig_destroy__ = _libBornAgainBase.delete_FixedBinAxis
 
 # Register FixedBinAxis in _libBornAgainBase:
 _libBornAgainBase.FixedBinAxis_swigregister(FixedBinAxis)
@@ -1963,14 +1961,6 @@ class DiscreteAxis(IAxis):
         __init__(DiscreteAxis self, std::string const & name, size_t n, double first, double last) -> DiscreteAxis
         """
         _libBornAgainBase.DiscreteAxis_swiginit(self, _libBornAgainBase.new_DiscreteAxis(*args))
-
-    def clone(self):
-        r"""clone(DiscreteAxis self) -> DiscreteAxis"""
-        return _libBornAgainBase.DiscreteAxis_clone(self)
-
-    def clip(self, lower, upper):
-        r"""clip(DiscreteAxis self, double lower, double upper)"""
-        return _libBornAgainBase.DiscreteAxis_clip(self, lower, upper)
     __swig_destroy__ = _libBornAgainBase.delete_DiscreteAxis
 
 # Register DiscreteAxis in _libBornAgainBase:
diff --git a/auto/Wrap/libBornAgainBase_wrap.cpp b/auto/Wrap/libBornAgainBase_wrap.cpp
index b3b3041dfdb4634ed55d33341b589687971f1b0d..7eb13f74d6c934ab60a8859d4ce59d10c9f0aaef 100644
--- a/auto/Wrap/libBornAgainBase_wrap.cpp
+++ b/auto/Wrap/libBornAgainBase_wrap.cpp
@@ -25290,22 +25290,35 @@ SWIGINTERN PyObject *Bin1D_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject
   return SWIG_Py_Void();
 }
 
-SWIGINTERN PyObject *_wrap_delete_IAxis(PyObject *self, PyObject *args) {
+SWIGINTERN PyObject *_wrap_new_IAxis(PyObject *self, PyObject *args) {
   PyObject *resultobj = 0;
-  IAxis *arg1 = (IAxis *) 0 ;
-  void *argp1 = 0 ;
-  int res1 = 0 ;
-  PyObject *swig_obj[1] ;
+  std::string arg1 ;
+  std::vector< Bin1D,std::allocator< Bin1D > > *arg2 = 0 ;
+  void *argp2 = 0 ;
+  int res2 = 0 ;
+  PyObject *swig_obj[2] ;
+  IAxis *result = 0 ;
   
-  if (!args) SWIG_fail;
-  swig_obj[0] = args;
-  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IAxis, SWIG_POINTER_DISOWN |  0 );
-  if (!SWIG_IsOK(res1)) {
-    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "delete_IAxis" "', argument " "1"" of type '" "IAxis *""'"); 
+  if (!SWIG_Python_UnpackTuple(args, "new_IAxis", 2, 2, swig_obj)) SWIG_fail;
+  {
+    std::string *ptr = (std::string *)0;
+    int res = SWIG_AsPtr_std_string(swig_obj[0], &ptr);
+    if (!SWIG_IsOK(res) || !ptr) {
+      SWIG_exception_fail(SWIG_ArgError((ptr ? res : SWIG_TypeError)), "in method '" "new_IAxis" "', argument " "1"" of type '" "std::string""'"); 
+    }
+    arg1 = *ptr;
+    if (SWIG_IsNewObj(res)) delete ptr;
   }
-  arg1 = reinterpret_cast< IAxis * >(argp1);
-  delete arg1;
-  resultobj = SWIG_Py_Void();
+  res2 = SWIG_ConvertPtr(swig_obj[1], &argp2, SWIGTYPE_p_std__vectorT_Bin1D_std__allocatorT_Bin1D_t_t,  0  | 0);
+  if (!SWIG_IsOK(res2)) {
+    SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "new_IAxis" "', argument " "2"" of type '" "std::vector< Bin1D,std::allocator< Bin1D > > const &""'"); 
+  }
+  if (!argp2) {
+    SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "new_IAxis" "', argument " "2"" of type '" "std::vector< Bin1D,std::allocator< Bin1D > > const &""'"); 
+  }
+  arg2 = reinterpret_cast< std::vector< Bin1D,std::allocator< Bin1D > > * >(argp2);
+  result = (IAxis *)new IAxis(arg1,(std::vector< Bin1D,std::allocator< Bin1D > > const &)*arg2);
+  resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_IAxis, SWIG_POINTER_NEW |  0 );
   return resultobj;
 fail:
   return NULL;
@@ -25693,7 +25706,53 @@ fail:
 }
 
 
-SWIGINTERN PyObject *_wrap_IAxis_clip__SWIG_0(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) {
+SWIGINTERN PyObject *_wrap_IAxis_isEquidistantCoverage(PyObject *self, PyObject *args) {
+  PyObject *resultobj = 0;
+  IAxis *arg1 = (IAxis *) 0 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  PyObject *swig_obj[1] ;
+  bool result;
+  
+  if (!args) SWIG_fail;
+  swig_obj[0] = args;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IAxis, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IAxis_isEquidistantCoverage" "', argument " "1"" of type '" "IAxis const *""'"); 
+  }
+  arg1 = reinterpret_cast< IAxis * >(argp1);
+  result = (bool)((IAxis const *)arg1)->isEquidistantCoverage();
+  resultobj = SWIG_From_bool(static_cast< bool >(result));
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_IAxis_isDiscrete(PyObject *self, PyObject *args) {
+  PyObject *resultobj = 0;
+  IAxis *arg1 = (IAxis *) 0 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  PyObject *swig_obj[1] ;
+  bool result;
+  
+  if (!args) SWIG_fail;
+  swig_obj[0] = args;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IAxis, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IAxis_isDiscrete" "', argument " "1"" of type '" "IAxis const *""'"); 
+  }
+  arg1 = reinterpret_cast< IAxis * >(argp1);
+  result = (bool)((IAxis const *)arg1)->isDiscrete();
+  resultobj = SWIG_From_bool(static_cast< bool >(result));
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_IAxis_clipped__SWIG_0(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) {
   PyObject *resultobj = 0;
   IAxis *arg1 = (IAxis *) 0 ;
   double arg2 ;
@@ -25704,68 +25763,70 @@ SWIGINTERN PyObject *_wrap_IAxis_clip__SWIG_0(PyObject *self, Py_ssize_t nobjs,
   int ecode2 = 0 ;
   double val3 ;
   int ecode3 = 0 ;
+  SwigValueWrapper< IAxis > result;
   
   if ((nobjs < 3) || (nobjs > 3)) SWIG_fail;
   res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IAxis, 0 |  0 );
   if (!SWIG_IsOK(res1)) {
-    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IAxis_clip" "', argument " "1"" of type '" "IAxis *""'"); 
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IAxis_clipped" "', argument " "1"" of type '" "IAxis const *""'"); 
   }
   arg1 = reinterpret_cast< IAxis * >(argp1);
   ecode2 = SWIG_AsVal_double(swig_obj[1], &val2);
   if (!SWIG_IsOK(ecode2)) {
-    SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "IAxis_clip" "', argument " "2"" of type '" "double""'");
+    SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "IAxis_clipped" "', argument " "2"" of type '" "double""'");
   } 
   arg2 = static_cast< double >(val2);
   ecode3 = SWIG_AsVal_double(swig_obj[2], &val3);
   if (!SWIG_IsOK(ecode3)) {
-    SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "IAxis_clip" "', argument " "3"" of type '" "double""'");
+    SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "IAxis_clipped" "', argument " "3"" of type '" "double""'");
   } 
   arg3 = static_cast< double >(val3);
-  (arg1)->clip(arg2,arg3);
-  resultobj = SWIG_Py_Void();
+  result = ((IAxis const *)arg1)->clipped(arg2,arg3);
+  resultobj = SWIG_NewPointerObj((new IAxis(result)), SWIGTYPE_p_IAxis, SWIG_POINTER_OWN |  0 );
   return resultobj;
 fail:
   return NULL;
 }
 
 
-SWIGINTERN PyObject *_wrap_IAxis_clip__SWIG_1(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) {
+SWIGINTERN PyObject *_wrap_IAxis_clipped__SWIG_1(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) {
   PyObject *resultobj = 0;
   IAxis *arg1 = (IAxis *) 0 ;
   std::pair< double,double > arg2 ;
   void *argp1 = 0 ;
   int res1 = 0 ;
+  SwigValueWrapper< IAxis > result;
   
   if ((nobjs < 2) || (nobjs > 2)) SWIG_fail;
   res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IAxis, 0 |  0 );
   if (!SWIG_IsOK(res1)) {
-    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IAxis_clip" "', argument " "1"" of type '" "IAxis *""'"); 
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IAxis_clipped" "', argument " "1"" of type '" "IAxis const *""'"); 
   }
   arg1 = reinterpret_cast< IAxis * >(argp1);
   {
     std::pair< double,double > *ptr = (std::pair< double,double > *)0;
     int res = swig::asptr(swig_obj[1], &ptr);
     if (!SWIG_IsOK(res) || !ptr) {
-      SWIG_exception_fail(SWIG_ArgError((ptr ? res : SWIG_TypeError)), "in method '" "IAxis_clip" "', argument " "2"" of type '" "std::pair< double,double >""'"); 
+      SWIG_exception_fail(SWIG_ArgError((ptr ? res : SWIG_TypeError)), "in method '" "IAxis_clipped" "', argument " "2"" of type '" "std::pair< double,double >""'"); 
     }
     arg2 = *ptr;
     if (SWIG_IsNewObj(res)) delete ptr;
   }
-  (arg1)->clip(arg2);
-  resultobj = SWIG_Py_Void();
+  result = ((IAxis const *)arg1)->clipped(arg2);
+  resultobj = SWIG_NewPointerObj((new IAxis(result)), SWIGTYPE_p_IAxis, SWIG_POINTER_OWN |  0 );
   return resultobj;
 fail:
   return NULL;
 }
 
 
-SWIGINTERN PyObject *_wrap_IAxis_clip(PyObject *self, PyObject *args) {
+SWIGINTERN PyObject *_wrap_IAxis_clipped(PyObject *self, PyObject *args) {
   Py_ssize_t argc;
   PyObject *argv[4] = {
     0
   };
   
-  if (!(argc = SWIG_Python_UnpackTuple(args, "IAxis_clip", 0, 3, argv))) SWIG_fail;
+  if (!(argc = SWIG_Python_UnpackTuple(args, "IAxis_clipped", 0, 3, argv))) SWIG_fail;
   --argc;
   if (argc == 2) {
     int _v = 0;
@@ -25776,7 +25837,7 @@ SWIGINTERN PyObject *_wrap_IAxis_clip(PyObject *self, PyObject *args) {
       int res = swig::asptr(argv[1], (std::pair< double,double >**)(0));
       _v = SWIG_CheckState(res);
       if (_v) {
-        return _wrap_IAxis_clip__SWIG_1(self, argc, argv);
+        return _wrap_IAxis_clipped__SWIG_1(self, argc, argv);
       }
     }
   }
@@ -25796,17 +25857,17 @@ SWIGINTERN PyObject *_wrap_IAxis_clip(PyObject *self, PyObject *args) {
           _v = SWIG_CheckState(res);
         }
         if (_v) {
-          return _wrap_IAxis_clip__SWIG_0(self, argc, argv);
+          return _wrap_IAxis_clipped__SWIG_0(self, argc, argv);
         }
       }
     }
   }
   
 fail:
-  SWIG_Python_RaiseOrModifyTypeError("Wrong number or type of arguments for overloaded function 'IAxis_clip'.\n"
+  SWIG_Python_RaiseOrModifyTypeError("Wrong number or type of arguments for overloaded function 'IAxis_clipped'.\n"
     "  Possible C/C++ prototypes are:\n"
-    "    IAxis::clip(double,double)\n"
-    "    IAxis::clip(std::pair< double,double >)\n");
+    "    IAxis::clipped(double,double) const\n"
+    "    IAxis::clipped(std::pair< double,double >) const\n");
   return 0;
 }
 
@@ -25846,6 +25907,28 @@ fail:
 }
 
 
+SWIGINTERN PyObject *_wrap_delete_IAxis(PyObject *self, PyObject *args) {
+  PyObject *resultobj = 0;
+  IAxis *arg1 = (IAxis *) 0 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  PyObject *swig_obj[1] ;
+  
+  if (!args) SWIG_fail;
+  swig_obj[0] = args;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IAxis, SWIG_POINTER_DISOWN |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "delete_IAxis" "', argument " "1"" of type '" "IAxis *""'"); 
+  }
+  arg1 = reinterpret_cast< IAxis * >(argp1);
+  delete arg1;
+  resultobj = SWIG_Py_Void();
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
 SWIGINTERN PyObject *IAxis_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
   PyObject *obj;
   if (!SWIG_Python_UnpackTuple(args, "swigregister", 1, 1, &obj)) return NULL;
@@ -25853,7 +25936,11 @@ SWIGINTERN PyObject *IAxis_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject
   return SWIG_Py_Void();
 }
 
-SWIGINTERN PyObject *_wrap_new_FixedBinAxis__SWIG_0(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) {
+SWIGINTERN PyObject *IAxis_swiginit(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  return SWIG_Python_InitShadowInstance(args);
+}
+
+SWIGINTERN PyObject *_wrap_new_FixedBinAxis(PyObject *self, PyObject *args) {
   PyObject *resultobj = 0;
   std::string *arg1 = 0 ;
   size_t arg2 ;
@@ -25866,9 +25953,10 @@ SWIGINTERN PyObject *_wrap_new_FixedBinAxis__SWIG_0(PyObject *self, Py_ssize_t n
   int ecode3 = 0 ;
   double val4 ;
   int ecode4 = 0 ;
+  PyObject *swig_obj[4] ;
   FixedBinAxis *result = 0 ;
   
-  if ((nobjs < 4) || (nobjs > 4)) SWIG_fail;
+  if (!SWIG_Python_UnpackTuple(args, "new_FixedBinAxis", 4, 4, swig_obj)) SWIG_fail;
   {
     std::string *ptr = (std::string *)0;
     res1 = SWIG_AsPtr_std_string(swig_obj[0], &ptr);
@@ -25927,149 +26015,6 @@ fail:
 }
 
 
-SWIGINTERN PyObject *_wrap_new_FixedBinAxis__SWIG_1(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) {
-  PyObject *resultobj = 0;
-  FixedBinAxis *arg1 = 0 ;
-  void *argp1 = 0 ;
-  int res1 = 0 ;
-  std::unique_ptr< FixedBinAxis > rvrdeleter1 ;
-  FixedBinAxis *result = 0 ;
-  
-  if ((nobjs < 1) || (nobjs > 1)) SWIG_fail;
-  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1, SWIGTYPE_p_FixedBinAxis, SWIG_POINTER_RELEASE |  0 );
-  if (!SWIG_IsOK(res1)) {
-    if (res1 == SWIG_ERROR_RELEASE_NOT_OWNED) {
-      SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "new_FixedBinAxis" "', cannot release ownership as memory is not owned for argument " "1"" of type '" "FixedBinAxis &&""'");
-    } else {
-      SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "new_FixedBinAxis" "', argument " "1"" of type '" "FixedBinAxis &&""'"); 
-    }
-  }
-  if (!argp1) {
-    SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "new_FixedBinAxis" "', argument " "1"" of type '" "FixedBinAxis &&""'"); 
-  }
-  arg1 = reinterpret_cast< FixedBinAxis * >(argp1);
-  rvrdeleter1.reset(arg1);
-  result = (FixedBinAxis *)new FixedBinAxis((FixedBinAxis &&)*arg1);
-  resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_FixedBinAxis, SWIG_POINTER_NEW |  0 );
-  return resultobj;
-fail:
-  return NULL;
-}
-
-
-SWIGINTERN PyObject *_wrap_new_FixedBinAxis(PyObject *self, PyObject *args) {
-  Py_ssize_t argc;
-  PyObject *argv[5] = {
-    0
-  };
-  
-  if (!(argc = SWIG_Python_UnpackTuple(args, "new_FixedBinAxis", 0, 4, argv))) SWIG_fail;
-  --argc;
-  if (argc == 1) {
-    int _v = 0;
-    void *vptr = 0;
-    int res = SWIG_ConvertPtr(argv[0], &vptr, SWIGTYPE_p_FixedBinAxis, SWIG_POINTER_NO_NULL);
-    _v = SWIG_CheckState(res);
-    if (_v) {
-      return _wrap_new_FixedBinAxis__SWIG_1(self, argc, argv);
-    }
-  }
-  if (argc == 4) {
-    int _v = 0;
-    int res = SWIG_AsPtr_std_string(argv[0], (std::string**)(0));
-    _v = SWIG_CheckState(res);
-    if (_v) {
-      {
-        int res = SWIG_AsVal_size_t(argv[1], NULL);
-        _v = SWIG_CheckState(res);
-      }
-      if (_v) {
-        {
-          int res = SWIG_AsVal_double(argv[2], NULL);
-          _v = SWIG_CheckState(res);
-        }
-        if (_v) {
-          {
-            int res = SWIG_AsVal_double(argv[3], NULL);
-            _v = SWIG_CheckState(res);
-          }
-          if (_v) {
-            return _wrap_new_FixedBinAxis__SWIG_0(self, argc, argv);
-          }
-        }
-      }
-    }
-  }
-  
-fail:
-  SWIG_Python_RaiseOrModifyTypeError("Wrong number or type of arguments for overloaded function 'new_FixedBinAxis'.\n"
-    "  Possible C/C++ prototypes are:\n"
-    "    FixedBinAxis::FixedBinAxis(std::string const &,size_t,double,double)\n"
-    "    FixedBinAxis::FixedBinAxis(FixedBinAxis &&)\n");
-  return 0;
-}
-
-
-SWIGINTERN PyObject *_wrap_FixedBinAxis_clone(PyObject *self, PyObject *args) {
-  PyObject *resultobj = 0;
-  FixedBinAxis *arg1 = (FixedBinAxis *) 0 ;
-  void *argp1 = 0 ;
-  int res1 = 0 ;
-  PyObject *swig_obj[1] ;
-  FixedBinAxis *result = 0 ;
-  
-  if (!args) SWIG_fail;
-  swig_obj[0] = args;
-  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_FixedBinAxis, 0 |  0 );
-  if (!SWIG_IsOK(res1)) {
-    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "FixedBinAxis_clone" "', argument " "1"" of type '" "FixedBinAxis const *""'"); 
-  }
-  arg1 = reinterpret_cast< FixedBinAxis * >(argp1);
-  result = (FixedBinAxis *)((FixedBinAxis const *)arg1)->clone();
-  resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_FixedBinAxis, 0 |  0 );
-  return resultobj;
-fail:
-  return NULL;
-}
-
-
-SWIGINTERN PyObject *_wrap_FixedBinAxis_clip(PyObject *self, PyObject *args) {
-  PyObject *resultobj = 0;
-  FixedBinAxis *arg1 = (FixedBinAxis *) 0 ;
-  double arg2 ;
-  double arg3 ;
-  void *argp1 = 0 ;
-  int res1 = 0 ;
-  double val2 ;
-  int ecode2 = 0 ;
-  double val3 ;
-  int ecode3 = 0 ;
-  PyObject *swig_obj[3] ;
-  
-  if (!SWIG_Python_UnpackTuple(args, "FixedBinAxis_clip", 3, 3, swig_obj)) SWIG_fail;
-  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_FixedBinAxis, 0 |  0 );
-  if (!SWIG_IsOK(res1)) {
-    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "FixedBinAxis_clip" "', argument " "1"" of type '" "FixedBinAxis *""'"); 
-  }
-  arg1 = reinterpret_cast< FixedBinAxis * >(argp1);
-  ecode2 = SWIG_AsVal_double(swig_obj[1], &val2);
-  if (!SWIG_IsOK(ecode2)) {
-    SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "FixedBinAxis_clip" "', argument " "2"" of type '" "double""'");
-  } 
-  arg2 = static_cast< double >(val2);
-  ecode3 = SWIG_AsVal_double(swig_obj[2], &val3);
-  if (!SWIG_IsOK(ecode3)) {
-    SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "FixedBinAxis_clip" "', argument " "3"" of type '" "double""'");
-  } 
-  arg3 = static_cast< double >(val3);
-  (arg1)->clip(arg2,arg3);
-  resultobj = SWIG_Py_Void();
-  return resultobj;
-fail:
-  return NULL;
-}
-
-
 SWIGINTERN PyObject *FixedBinAxis_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
   PyObject *obj;
   if (!SWIG_Python_UnpackTuple(args, "swigregister", 1, 1, &obj)) return NULL;
@@ -26232,66 +26177,6 @@ fail:
 }
 
 
-SWIGINTERN PyObject *_wrap_DiscreteAxis_clone(PyObject *self, PyObject *args) {
-  PyObject *resultobj = 0;
-  DiscreteAxis *arg1 = (DiscreteAxis *) 0 ;
-  void *argp1 = 0 ;
-  int res1 = 0 ;
-  PyObject *swig_obj[1] ;
-  DiscreteAxis *result = 0 ;
-  
-  if (!args) SWIG_fail;
-  swig_obj[0] = args;
-  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_DiscreteAxis, 0 |  0 );
-  if (!SWIG_IsOK(res1)) {
-    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "DiscreteAxis_clone" "', argument " "1"" of type '" "DiscreteAxis const *""'"); 
-  }
-  arg1 = reinterpret_cast< DiscreteAxis * >(argp1);
-  result = (DiscreteAxis *)((DiscreteAxis const *)arg1)->clone();
-  resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_DiscreteAxis, 0 |  0 );
-  return resultobj;
-fail:
-  return NULL;
-}
-
-
-SWIGINTERN PyObject *_wrap_DiscreteAxis_clip(PyObject *self, PyObject *args) {
-  PyObject *resultobj = 0;
-  DiscreteAxis *arg1 = (DiscreteAxis *) 0 ;
-  double arg2 ;
-  double arg3 ;
-  void *argp1 = 0 ;
-  int res1 = 0 ;
-  double val2 ;
-  int ecode2 = 0 ;
-  double val3 ;
-  int ecode3 = 0 ;
-  PyObject *swig_obj[3] ;
-  
-  if (!SWIG_Python_UnpackTuple(args, "DiscreteAxis_clip", 3, 3, swig_obj)) SWIG_fail;
-  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_DiscreteAxis, 0 |  0 );
-  if (!SWIG_IsOK(res1)) {
-    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "DiscreteAxis_clip" "', argument " "1"" of type '" "DiscreteAxis *""'"); 
-  }
-  arg1 = reinterpret_cast< DiscreteAxis * >(argp1);
-  ecode2 = SWIG_AsVal_double(swig_obj[1], &val2);
-  if (!SWIG_IsOK(ecode2)) {
-    SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "DiscreteAxis_clip" "', argument " "2"" of type '" "double""'");
-  } 
-  arg2 = static_cast< double >(val2);
-  ecode3 = SWIG_AsVal_double(swig_obj[2], &val3);
-  if (!SWIG_IsOK(ecode3)) {
-    SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "DiscreteAxis_clip" "', argument " "3"" of type '" "double""'");
-  } 
-  arg3 = static_cast< double >(val3);
-  (arg1)->clip(arg2,arg3);
-  resultobj = SWIG_Py_Void();
-  return resultobj;
-fail:
-  return NULL;
-}
-
-
 SWIGINTERN PyObject *_wrap_delete_DiscreteAxis(PyObject *self, PyObject *args) {
   PyObject *resultobj = 0;
   DiscreteAxis *arg1 = (DiscreteAxis *) 0 ;
@@ -28778,7 +28663,7 @@ static PyMethodDef SwigMethods[] = {
 	 { "Bin1D_clipped_or_nil", _wrap_Bin1D_clipped_or_nil, METH_VARARGS, "Bin1D_clipped_or_nil(Bin1D self, double lower, double upper) -> std::optional< Bin1D >"},
 	 { "delete_Bin1D", _wrap_delete_Bin1D, METH_O, "delete_Bin1D(Bin1D self)"},
 	 { "Bin1D_swigregister", Bin1D_swigregister, METH_O, NULL},
-	 { "delete_IAxis", _wrap_delete_IAxis, METH_O, "delete_IAxis(IAxis self)"},
+	 { "new_IAxis", _wrap_new_IAxis, METH_VARARGS, "new_IAxis(std::string name, std::vector< Bin1D,std::allocator< Bin1D > > const & bins) -> IAxis"},
 	 { "IAxis_clone", _wrap_IAxis_clone, METH_O, "IAxis_clone(IAxis self) -> IAxis"},
 	 { "IAxis_setAxisName", _wrap_IAxis_setAxisName, METH_VARARGS, "IAxis_setAxisName(IAxis self, std::string name)"},
 	 { "IAxis_axisName", _wrap_IAxis_axisName, METH_O, "IAxis_axisName(IAxis self) -> std::string"},
@@ -28794,27 +28679,24 @@ static PyMethodDef SwigMethods[] = {
 	 { "IAxis_bins", _wrap_IAxis_bins, METH_O, "IAxis_bins(IAxis self) -> std::vector< Bin1D,std::allocator< Bin1D > > const &"},
 	 { "IAxis_binCenters", _wrap_IAxis_binCenters, METH_O, "IAxis_binCenters(IAxis self) -> vdouble1d_t"},
 	 { "IAxis_closestIndex", _wrap_IAxis_closestIndex, METH_VARARGS, "IAxis_closestIndex(IAxis self, double value) -> size_t"},
-	 { "IAxis_clip", _wrap_IAxis_clip, METH_VARARGS, "\n"
-		"IAxis_clip(IAxis self, double lower, double upper)\n"
-		"IAxis_clip(IAxis self, pvacuum_double_t bounds)\n"
+	 { "IAxis_isEquidistantCoverage", _wrap_IAxis_isEquidistantCoverage, METH_O, "IAxis_isEquidistantCoverage(IAxis self) -> bool"},
+	 { "IAxis_isDiscrete", _wrap_IAxis_isDiscrete, METH_O, "IAxis_isDiscrete(IAxis self) -> bool"},
+	 { "IAxis_clipped", _wrap_IAxis_clipped, METH_VARARGS, "\n"
+		"IAxis_clipped(IAxis self, double lower, double upper) -> IAxis\n"
+		"IAxis_clipped(IAxis self, pvacuum_double_t bounds) -> IAxis\n"
 		""},
 	 { "IAxis___eq__", _wrap_IAxis___eq__, METH_VARARGS, "IAxis___eq__(IAxis self, IAxis right) -> bool"},
+	 { "delete_IAxis", _wrap_delete_IAxis, METH_O, "delete_IAxis(IAxis self)"},
 	 { "IAxis_swigregister", IAxis_swigregister, METH_O, NULL},
+	 { "IAxis_swiginit", IAxis_swiginit, METH_VARARGS, NULL},
+	 { "new_FixedBinAxis", _wrap_new_FixedBinAxis, METH_VARARGS, "new_FixedBinAxis(std::string const & name, size_t nbins, double start, double end) -> FixedBinAxis"},
 	 { "delete_FixedBinAxis", _wrap_delete_FixedBinAxis, METH_O, "delete_FixedBinAxis(FixedBinAxis self)"},
-	 { "new_FixedBinAxis", _wrap_new_FixedBinAxis, METH_VARARGS, "\n"
-		"FixedBinAxis(std::string const & name, size_t nbins, double start, double end)\n"
-		"new_FixedBinAxis(FixedBinAxis arg1) -> FixedBinAxis\n"
-		""},
-	 { "FixedBinAxis_clone", _wrap_FixedBinAxis_clone, METH_O, "FixedBinAxis_clone(FixedBinAxis self) -> FixedBinAxis"},
-	 { "FixedBinAxis_clip", _wrap_FixedBinAxis_clip, METH_VARARGS, "FixedBinAxis_clip(FixedBinAxis self, double lower, double upper)"},
 	 { "FixedBinAxis_swigregister", FixedBinAxis_swigregister, METH_O, NULL},
 	 { "FixedBinAxis_swiginit", FixedBinAxis_swiginit, METH_VARARGS, NULL},
 	 { "new_DiscreteAxis", _wrap_new_DiscreteAxis, METH_VARARGS, "\n"
 		"DiscreteAxis(std::string const & name, vdouble1d_t points)\n"
 		"new_DiscreteAxis(std::string const & name, size_t n, double first, double last) -> DiscreteAxis\n"
 		""},
-	 { "DiscreteAxis_clone", _wrap_DiscreteAxis_clone, METH_O, "DiscreteAxis_clone(DiscreteAxis self) -> DiscreteAxis"},
-	 { "DiscreteAxis_clip", _wrap_DiscreteAxis_clip, METH_VARARGS, "DiscreteAxis_clip(DiscreteAxis self, double lower, double upper)"},
 	 { "delete_DiscreteAxis", _wrap_delete_DiscreteAxis, METH_O, "delete_DiscreteAxis(DiscreteAxis self)"},
 	 { "DiscreteAxis_swigregister", DiscreteAxis_swigregister, METH_O, NULL},
 	 { "DiscreteAxis_swiginit", DiscreteAxis_swiginit, METH_VARARGS, NULL},