diff --git a/Base/Axis/Frame.cpp b/Base/Axis/Frame.cpp
index 38847e0d0f0350a154d937f563d12d442d1a50b7..ddc66982a7660c6be867a71a58333af9143ba5d1 100644
--- a/Base/Axis/Frame.cpp
+++ b/Base/Axis/Frame.cpp
@@ -165,3 +165,9 @@ Frame* Frame::flat() const
             outaxes.emplace_back(s->clone());
     return new Frame(std::move(outaxes));
 }
+
+void Frame::setScale(size_t k_axis, Scale* scale)
+{
+    m_axes.replace_at(k_axis, scale);
+    m_size = FrameUtil::product_size(m_axes.reference());
+}
diff --git a/Base/Axis/Frame.h b/Base/Axis/Frame.h
index 58215319a14f5813120285e8be8b43883e65c677..5cbc1e9d9ed8df921267f47f65a86a4276aaaa8b 100644
--- a/Base/Axis/Frame.h
+++ b/Base/Axis/Frame.h
@@ -83,6 +83,7 @@ public:
     Frame* flat() const;
 
 #ifndef SWIG
+    void setScale(size_t k_axis, Scale* scale);
     std::vector<const Scale*> clonedAxes() const;
 
 private:
diff --git a/Base/Types/OwningVector.h b/Base/Types/OwningVector.h
index bac97140c22896f1944a71999f2b99172e91928d..ca8e04b40c2614adde7fa2389e2e2d38c6b470fc 100644
--- a/Base/Types/OwningVector.h
+++ b/Base/Types/OwningVector.h
@@ -18,6 +18,7 @@
 #ifndef BORNAGAIN_BASE_TYPES_OWNINGVECTOR_H
 #define BORNAGAIN_BASE_TYPES_OWNINGVECTOR_H
 
+#include "Base/Util/Assert.h"
 #include <cstddef>
 #include <utility>
 #include <vector>
@@ -45,9 +46,6 @@ public:
     OwningVector& operator=(OwningVector&& other) = default;
 
     void reserve(size_t n) { m_v.reserve(n); }
-    void emplace_back(T* e) { m_v.emplace_back(e); }
-    void insert_at(size_t index, T* e) { m_v.insert(m_v.begin() + index, e); }
-
     void clear()
     {
         for (T* e : *this)
@@ -82,6 +80,15 @@ public:
         m_v.erase(m_v.begin() + i);
         return result;
     }
+    void emplace_back(T* e) { m_v.emplace_back(e); }
+    void insert_at(size_t i, T* e) { m_v.insert(m_v.begin() + i, e); }
+    void replace_at(size_t i, T* e)
+    {
+        ASSERT(i < m_v.size());
+        delete m_v[i];
+        m_v[i] = e;
+    }
+
 
     T* release_back()
     {
diff --git a/Device/Data/Datafield.cpp b/Device/Data/Datafield.cpp
index 08038dc68f1140db86514f46b4784b73a4334790..e1429ba1347a32beb38748eed8f183db1899a7d0 100644
--- a/Device/Data/Datafield.cpp
+++ b/Device/Data/Datafield.cpp
@@ -255,6 +255,7 @@ double Datafield::minVal() const
 
 Datafield* Datafield::crop(double xmin, double ymin, double xmax, double ymax) const
 {
+    ASSERT(false); // needs renovation, similar to Datafield::crop(double xmin, double xmax)
     const auto xclipped = std::make_unique<Scale>(xAxis().clipped(xmin, xmax));
     const auto yclipped = std::make_unique<Scale>(yAxis().clipped(ymin, ymax));
 
@@ -271,16 +272,30 @@ Datafield* Datafield::crop(double xmin, double ymin, double xmax, double ymax) c
 
 Datafield* Datafield::crop(double xmin, double xmax) const
 {
-    const auto xclipped = std::make_unique<Scale>(xAxis().clipped(xmin, xmax));
+    std::vector<Bin1D> outbins;
+    for (size_t i = 0; i < xAxis().size(); ++i) {
+        const double x = xAxis().binCenter(i);
+        if (xmin <= x && x <= xmax)
+            outbins.push_back(xAxis().bin(i));
+    }
 
-    std::vector<double> out(size());
-    size_t iout = 0;
-    for (size_t i = 0; i < size(); ++i) {
+    auto* xclipped = new Scale(xAxis().axisLabel(), outbins);
+
+    const size_t N = size();
+    std::vector<double> out;
+    std::vector<double> errout;
+    for (size_t i = 0; i < N; ++i) {
         const double x = frame().projectedCoord(i, 0);
-        if (xclipped->rangeComprises(x))
-            out[iout++] = m_values[i];
+        if (xmin <= x && x <= xmax) {
+            out.push_back(m_values[i]);
+            if (hasErrorSigmas())
+                errout.push_back(m_errSigmas[i]);
+        }
     }
-    return new Datafield(frame().clone(), out);
+    Frame* outframe = frame().clone();
+    outframe->setScale(0, xclipped);
+    ASSERT(outframe->xAxis().size() == out.size());
+    return new Datafield(outframe, out, errout);
 }
 
 #ifdef BORNAGAIN_PYTHON
diff --git a/Device/IO/ReadReflectometry.cpp b/Device/IO/ReadReflectometry.cpp
index 71343ca33773993265584e7244fafc7c5cb90a57..4cf7920691057b290cf9e2da94cad0f059706796 100644
--- a/Device/IO/ReadReflectometry.cpp
+++ b/Device/IO/ReadReflectometry.cpp
@@ -69,7 +69,7 @@ Datafield* Util::RW::readReflectometryTable(std::istream& s, const ImportSetting
     double fac = 1.;
     Coordinate outCoord = p.xCoord;
     if (outCoord.unit() == "1/angstrom") {
-        fac = 0.1;
+        fac = 10;
         outCoord = {outCoord.name(), "1/nm"};
     } else if (outCoord.unit() == "deg") {
         fac = pi / 180.;
diff --git a/GUI/Model/Model/ParameterTreeUtil.cpp b/GUI/Model/Model/ParameterTreeUtil.cpp
index 9c3e106882c749c243704c291be6b19f8862c443..54671288089ea0670d2563fb1ea9c568ce046d26 100644
--- a/GUI/Model/Model/ParameterTreeUtil.cpp
+++ b/GUI/Model/Model/ParameterTreeUtil.cpp
@@ -43,7 +43,7 @@ QString labelWithUnit(const QString& label, variant<QString, Unit> unit)
                                                             : unitAsString(std::get<Unit>(unit));
 
     if (!s.isEmpty())
-        return label + " [" + s + "]";
+        return label + " (" + s + ")";
 
     return label;
 }
diff --git a/GUI/View/MaterialEditor/MaterialEditorModel.cpp b/GUI/View/MaterialEditor/MaterialEditorModel.cpp
index 34138a5ca9f51b20e3f21eda2fce19a1c9f9ac3a..3714fa455d6cf19e914b48bcde28053ed5f3e077 100644
--- a/GUI/View/MaterialEditor/MaterialEditorModel.cpp
+++ b/GUI/View/MaterialEditor/MaterialEditorModel.cpp
@@ -48,7 +48,7 @@ QVariant MaterialEditorModel::headerData(int section, Qt::Orientation /*orientat
     case PARAMETERS:
         return "Material parameters";
     case MAGNETIZATION:
-        return "Magnetization [A/m]";
+        return "Magnetization (A/m)";
     default:
         return {};
     }
diff --git a/GUI/View/Numeric/NumWidgetUtil.cpp b/GUI/View/Numeric/NumWidgetUtil.cpp
index 68110dd28bad3d91bb07ddef5f0896571c4a5c92..0be3617e5fed7ec0e70978a04305c5f4d9fc1b72 100644
--- a/GUI/View/Numeric/NumWidgetUtil.cpp
+++ b/GUI/View/Numeric/NumWidgetUtil.cpp
@@ -44,7 +44,7 @@ QString GUI::Util::labelWithUnit(const QString& label, variant<QString, Unit> un
     const QString s = std::holds_alternative<QString>(unit) ? std::get<QString>(unit)
                                                             : unitAsString(std::get<Unit>(unit));
     if (!s.isEmpty())
-        return label + " [" + s + "]";
+        return label + " (" + s + ")";
 
     return label;
 }
diff --git a/GUI/View/SampleDesigner/LayerEditorUtil.cpp b/GUI/View/SampleDesigner/LayerEditorUtil.cpp
index 2667f1380b84c16f66d3dc6c08ec5db59abaa70a..dfb442e5874b0917f480390bb4540f6e3da02f28 100644
--- a/GUI/View/SampleDesigner/LayerEditorUtil.cpp
+++ b/GUI/View/SampleDesigner/LayerEditorUtil.cpp
@@ -46,7 +46,7 @@ void updateLabelUnit(QLabel* label, const QString& unit)
         text.chop(1);
 
     if (!unit.isEmpty())
-        text += " [" + unit + "]";
+        text += " (" + unit + ")";
     if (hasColon)
         text += ":";
     label->setText(text);
diff --git a/auto/Examples/fit/specular/Honeycomb_fit.py b/auto/Examples/fit/specular/Honeycomb_fit.py
index 13020d2e508b3cded972e011c84ac6008e3e0b8e..9a9a01df3d491da0885dbd9cb03280343d79e4f1 100755
--- a/auto/Examples/fit/specular/Honeycomb_fit.py
+++ b/auto/Examples/fit/specular/Honeycomb_fit.py
@@ -139,11 +139,11 @@ def plot(q, rs, exps, shifts, labels):
     ax.set_yscale('log')
     plt.legend()
 
-    plt.xlabel("Q [nm${}^{-1}$]")
+    plt.xlabel("q (1/nm)")
     plt.ylabel("R")
 
     plt.tight_layout()
-    plt.close()
+    #plt.close()
 
 
 def plot_sld_profile(P):
@@ -159,12 +159,12 @@ def plot_sld_profile(P):
     plt.plot(z_150p, np.real(sld_150p)*1e6, label=r"150K $+$")
     plt.plot(z_150m, np.real(sld_150m)*1e6, label=r"150K $-$")
 
-    plt.xlabel(r"$z$ [A]")
+    plt.xlabel(r"$z (\AA)$")
     plt.ylabel(r"$\delta(z) \cdot 10^6$")
 
     plt.legend()
     plt.tight_layout()
-    plt.close()
+    #plt.close()
 
 ####################################################################
 #  Main
@@ -181,18 +181,6 @@ if __name__ == '__main__':
         "t_Py2": (56, 46, 66),
         "t_Py1": (56, 46, 66),
         "t_SiO2": (22, 15, 29),
-        "sld_PyOx_real": (1.995, 1.92, 2.07),
-        "sld_Py2_real": (5, 4.7, 5.3),
-        "sld_Py1_real": (4.62, 4.32, 4.92),
-        "rPyOx": (27, 15, 35),
-        "rPy2": (12, 2, 20),
-        "rPy1": (12, 2, 20),
-        "rSiO2": (15, 5, 25),
-        "rSi": (15, 5, 25),
-        "msld_PyOx": (0.25, 0, 1),
-        "msld_Py2": (0.63, 0, 1),
-        "msld_Py1": (0.64, 0, 1),
-        "ms150": (1.05, 1.0, 1.1),
     }
 
     # For fixed parameters, bounds are ignored. We leave them here just
@@ -206,6 +194,19 @@ if __name__ == '__main__':
         "sld_SiO2_real": (3.47, 3, 4),
         "sld_Si_real": (2.0704, 2, 3),
         "dq": (0.018, 0, 0.1),
+    # Start by moving the following back to startPnB:
+        "sld_PyOx_real": (1.995, 1.92, 2.07),
+        "sld_Py2_real": (5, 4.7, 5.3),
+        "sld_Py1_real": (4.62, 4.32, 4.92),
+        "rPyOx": (27, 15, 35),
+        "rPy2": (12, 2, 20),
+        "rPy1": (12, 2, 20),
+        "rSiO2": (15, 5, 25),
+        "rSi": (15, 5, 25),
+        "msld_PyOx": (0.25, 0, 1),
+        "msld_Py2": (0.63, 0, 1),
+        "msld_Py1": (0.64, 0, 1),
+        "ms150": (1.05, 1.0, 1.1),
     }
 
     fixedP = {d: v[0] for d, v in fixedPnB.items()}
@@ -261,8 +262,8 @@ if __name__ == '__main__':
     result = scipy.optimize.differential_evolution(
         objective_function,
         bounds,
-        maxiter=500,
-        popsize=10,
+        maxiter=5, # for a serious DE fit, choose 500
+        popsize=3, # for a serious DE fit, choose 10
         tol=1e-2,
         mutation=(0.5, 1.5),
         seed=0,
diff --git a/auto/Examples/fit/specular/PolarizedSpinAsymmetry.py b/auto/Examples/fit/specular/PolarizedSpinAsymmetry.py
index 91f3571438b9c1f013a7101089422ce6b7d8502c..d859c5ea1d5954e5a9c88d36021a46d1b46092f2 100755
--- a/auto/Examples/fit/specular/PolarizedSpinAsymmetry.py
+++ b/auto/Examples/fit/specular/PolarizedSpinAsymmetry.py
@@ -131,7 +131,7 @@ def plotData(qs, rs, exps, labels, colors):
     ax.set_yscale('log')
     plt.legend()
 
-    plt.xlabel("Q [nm${}^{-1}$]")
+    plt.xlabel("q (1/nm)")
     plt.ylabel("R")
 
     plt.tight_layout()
@@ -164,7 +164,7 @@ def plotSpinAsymmetry(data_pp, data_mm, q, r_pp, r_mm):
 
     plt.gca().set_ylim((-0.3, 0.5))
 
-    plt.xlabel("Q [nm${}^{-1}$]")
+    plt.xlabel("Q (1/nm)")
     plt.ylabel("Spin asymmetry")
 
     plt.tight_layout()
diff --git a/auto/Examples/fit/specular/Pt_layer_fit.py b/auto/Examples/fit/specular/Pt_layer_fit.py
index a9b82577a8b3665631eb793b3af5daecdedb1e41..1af4988e7659c3c294d4abd9d0ea38aabf298eee 100755
--- a/auto/Examples/fit/specular/Pt_layer_fit.py
+++ b/auto/Examples/fit/specular/Pt_layer_fit.py
@@ -47,6 +47,7 @@ def get_simulation(q_axis, P):
     sample = get_sample(P)
 
     scan = ba.QzScan(q_axis)
+    scan.setIntensity(P["intensity"])
     scan.setOffset(P["q_offset"])
 
     distr = ba.DistributionGaussian(0., 1., 25, 4.)
@@ -56,44 +57,18 @@ def get_simulation(q_axis, P):
 
     return simulation
 
-####################################################################
-#  Experimental data
-####################################################################
-
-def load_data(filename, qmin, qmax):
-    filepath = os.path.join(datadir, filename)
-    data = np.genfromtxt(filepath, unpack=True)
-
-    r0 = np.where(data[0] - np.roll(data[0], 1) == 0)
-    data = np.delete(data, r0, 1)
-
-    data[0] = data[0]/angstrom
-    data[3] = data[3]/angstrom
-
-    data[1] = data[1]
-    data[2] = data[2]
-
-    so = np.argsort(data[0])
-
-    data = data[:, so]
-
-    minIndex = np.argmin(np.abs(data[0] - qmin))
-    maxIndex = np.argmin(np.abs(data[0] - qmax))
-
-    return data[:, minIndex:maxIndex + 1]
-
 ####################################################################
 #  Plotting
 ####################################################################
 
-def plot(q, r, exp, P):
+def plot(q, r, data, P):
     fig = plt.figure()
     ax = fig.add_subplot(111)
 
-    ax.errorbar(exp[0],
-                exp[1],
-                xerr=exp[3],
-                yerr=exp[2],
+    ax.errorbar(data.npXcenters(),
+                data.npArray(),
+                # xerr=data.xxx, TODO restore
+                yerr=data.npErrors(),
                 label="R",
                 fmt='.',
                 markersize=1.,
@@ -104,7 +79,7 @@ def plot(q, r, exp, P):
 
     ax.set_yscale('log')
 
-    ax.set_xlabel("Q [nm$^{^-1}$]")
+    ax.set_xlabel("q (1/nm)")
     ax.set_ylabel("R")
 
     y = 0.5
@@ -129,6 +104,7 @@ if __name__ == '__main__':
     }
 
     startPnB = {
+        "intensity": (1., 0.8, 1.2),
         "q_offset": (0.01, -0.02, 0.02),
         "q_res/q": (0.01, 0, 0.02),
         "t_pt/nm": (50, 45, 55),
@@ -143,25 +119,28 @@ if __name__ == '__main__':
 
     qmin = 0.18
     qmax = 2.4
-
-    data = load_data("specular/RvsQ_36563_36662.dat.gz", qmin, qmax)
-
     qzs = np.linspace(qmin, qmax, 1500)
 
-    # Plot data with initial model:
+    fpath = os.path.join(datadir, "specular/RvsQ_36563_36662.dat.gz")
+    flags = ba.ImportSettings1D("q (1/angstrom)", "#", "", 1, 2, 3, 4)
+    data = ba.readData1D(fpath, ba.csv1D, flags)
+
+    # Initial plot
 
     r = get_simulation(qzs, initialP | fixedP).simulate().npArray()
     plot(qzs, r, data, initialP)
 
-    # Fit:
+    # Restrict data to given q range
 
-    Q, R, dR = (data[0], data[1], data[2])
+    data = data.crop(qmin, qmax)
+
+    # Fit:
 
     fit_objective = ba.FitObjective()
     fit_objective.setObjectiveMetric("chi2")
     fit_objective.initPrint(10)
-    fit_objective.addSimulationAndData(
-        lambda P: get_simulation(Q, P | fixedP), R, dR, 1)
+    fit_objective.addFitPair(
+        lambda P: get_simulation(data.npXcenters(), P | fixedP), data, 1)
 
     P = ba.Parameters()
     for name, p in startPnB.items():
diff --git a/auto/Examples/specular/PolarizedSpinAsymmetry.py b/auto/Examples/specular/PolarizedSpinAsymmetry.py
index 91f3571438b9c1f013a7101089422ce6b7d8502c..d859c5ea1d5954e5a9c88d36021a46d1b46092f2 100755
--- a/auto/Examples/specular/PolarizedSpinAsymmetry.py
+++ b/auto/Examples/specular/PolarizedSpinAsymmetry.py
@@ -131,7 +131,7 @@ def plotData(qs, rs, exps, labels, colors):
     ax.set_yscale('log')
     plt.legend()
 
-    plt.xlabel("Q [nm${}^{-1}$]")
+    plt.xlabel("q (1/nm)")
     plt.ylabel("R")
 
     plt.tight_layout()
@@ -164,7 +164,7 @@ def plotSpinAsymmetry(data_pp, data_mm, q, r_pp, r_mm):
 
     plt.gca().set_ylim((-0.3, 0.5))
 
-    plt.xlabel("Q [nm${}^{-1}$]")
+    plt.xlabel("Q (1/nm)")
     plt.ylabel("Spin asymmetry")
 
     plt.tight_layout()
diff --git a/auto/MiniExamples/fit/specular/Honeycomb_fit.py b/auto/MiniExamples/fit/specular/Honeycomb_fit.py
index c6cbc5a32c6241bc1e51acf157c7d9d9460baf22..b07ea2c3d0084de2b9aea5765b77109e7d1a5ac7 100755
--- a/auto/MiniExamples/fit/specular/Honeycomb_fit.py
+++ b/auto/MiniExamples/fit/specular/Honeycomb_fit.py
@@ -139,11 +139,11 @@ def plot(q, rs, exps, shifts, labels):
     ax.set_yscale('log')
     plt.legend()
 
-    plt.xlabel("Q [nm${}^{-1}$]")
+    plt.xlabel("q (1/nm)")
     plt.ylabel("R")
 
     #plt.tight_layout()
-    #plt.close()
+    ##plt.close()
 
 
 def plot_sld_profile(P):
@@ -159,12 +159,12 @@ def plot_sld_profile(P):
     plt.plot(z_150p, np.real(sld_150p)*1e6, label=r"150K $+$")
     plt.plot(z_150m, np.real(sld_150m)*1e6, label=r"150K $-$")
 
-    plt.xlabel(r"$z$ [A]")
+    plt.xlabel(r"$z (\AA)$")
     plt.ylabel(r"$\delta(z) \cdot 10^6$")
 
     plt.legend()
     #plt.tight_layout()
-    #plt.close()
+    ##plt.close()
 
 ####################################################################
 #  Main
@@ -181,18 +181,6 @@ if __name__ == '__main__':
         "t_Py2": (56, 46, 66),
         "t_Py1": (56, 46, 66),
         "t_SiO2": (22, 15, 29),
-        "sld_PyOx_real": (1.995, 1.92, 2.07),
-        "sld_Py2_real": (5, 4.7, 5.3),
-        "sld_Py1_real": (4.62, 4.32, 4.92),
-        "rPyOx": (27, 15, 35),
-        "rPy2": (12, 2, 20),
-        "rPy1": (12, 2, 20),
-        "rSiO2": (15, 5, 25),
-        "rSi": (15, 5, 25),
-        "msld_PyOx": (0.25, 0, 1),
-        "msld_Py2": (0.63, 0, 1),
-        "msld_Py1": (0.64, 0, 1),
-        "ms150": (1.05, 1.0, 1.1),
     }
 
     # For fixed parameters, bounds are ignored. We leave them here just
@@ -206,6 +194,19 @@ if __name__ == '__main__':
         "sld_SiO2_real": (3.47, 3, 4),
         "sld_Si_real": (2.0704, 2, 3),
         "dq": (0.018, 0, 0.1),
+    # Start by moving the following back to startPnB:
+        "sld_PyOx_real": (1.995, 1.92, 2.07),
+        "sld_Py2_real": (5, 4.7, 5.3),
+        "sld_Py1_real": (4.62, 4.32, 4.92),
+        "rPyOx": (27, 15, 35),
+        "rPy2": (12, 2, 20),
+        "rPy1": (12, 2, 20),
+        "rSiO2": (15, 5, 25),
+        "rSi": (15, 5, 25),
+        "msld_PyOx": (0.25, 0, 1),
+        "msld_Py2": (0.63, 0, 1),
+        "msld_Py1": (0.64, 0, 1),
+        "ms150": (1.05, 1.0, 1.1),
     }
 
     fixedP = {d: v[0] for d, v in fixedPnB.items()}
@@ -261,8 +262,8 @@ if __name__ == '__main__':
     result = scipy.optimize.differential_evolution(
         objective_function,
         bounds,
-        maxiter=2,
-        popsize=2,
+        maxiter=2, # for a serious DE fit, choose 500
+        popsize=2, # for a serious DE fit, choose 10
         tol=1e-2,
         mutation=(0.5, 1.5),
         seed=0,
diff --git a/auto/MiniExamples/fit/specular/PolarizedSpinAsymmetry.py b/auto/MiniExamples/fit/specular/PolarizedSpinAsymmetry.py
index 0123529d57f5d1fbcc58a736a26deb88705e0d01..3185a2719f2c01d1ab3a1ddb56083135a0937efa 100755
--- a/auto/MiniExamples/fit/specular/PolarizedSpinAsymmetry.py
+++ b/auto/MiniExamples/fit/specular/PolarizedSpinAsymmetry.py
@@ -131,7 +131,7 @@ def plotData(qs, rs, exps, labels, colors):
     ax.set_yscale('log')
     plt.legend()
 
-    plt.xlabel("Q [nm${}^{-1}$]")
+    plt.xlabel("q (1/nm)")
     plt.ylabel("R")
 
     plt.tight_layout()
@@ -164,7 +164,7 @@ def plotSpinAsymmetry(data_pp, data_mm, q, r_pp, r_mm):
 
     plt.gca().set_ylim((-0.3, 0.5))
 
-    plt.xlabel("Q [nm${}^{-1}$]")
+    plt.xlabel("Q (1/nm)")
     plt.ylabel("Spin asymmetry")
 
     plt.tight_layout()
diff --git a/auto/MiniExamples/fit/specular/Pt_layer_fit.py b/auto/MiniExamples/fit/specular/Pt_layer_fit.py
index b64d78e7e0813cd56a865fef7c1bfc5316ee46e1..6370c896f527e8ca7534253bfb0a0ecf3f1f16b4 100755
--- a/auto/MiniExamples/fit/specular/Pt_layer_fit.py
+++ b/auto/MiniExamples/fit/specular/Pt_layer_fit.py
@@ -47,6 +47,7 @@ def get_simulation(q_axis, P):
     sample = get_sample(P)
 
     scan = ba.QzScan(q_axis)
+    scan.setIntensity(P["intensity"])
     scan.setOffset(P["q_offset"])
 
     distr = ba.DistributionGaussian(0., 1., 25, 4.)
@@ -56,44 +57,18 @@ def get_simulation(q_axis, P):
 
     return simulation
 
-####################################################################
-#  Experimental data
-####################################################################
-
-def load_data(filename, qmin, qmax):
-    filepath = os.path.join(datadir, filename)
-    data = np.genfromtxt(filepath, unpack=True)
-
-    r0 = np.where(data[0] - np.roll(data[0], 1) == 0)
-    data = np.delete(data, r0, 1)
-
-    data[0] = data[0]/angstrom
-    data[3] = data[3]/angstrom
-
-    data[1] = data[1]
-    data[2] = data[2]
-
-    so = np.argsort(data[0])
-
-    data = data[:, so]
-
-    minIndex = np.argmin(np.abs(data[0] - qmin))
-    maxIndex = np.argmin(np.abs(data[0] - qmax))
-
-    return data[:, minIndex:maxIndex + 1]
-
 ####################################################################
 #  Plotting
 ####################################################################
 
-def plot(q, r, exp, P):
+def plot(q, r, data, P):
     fig = plt.figure()
     ax = fig.add_subplot(111)
 
-    ax.errorbar(exp[0],
-                exp[1],
-                xerr=exp[3],
-                yerr=exp[2],
+    ax.errorbar(data.npXcenters(),
+                data.npArray(),
+                # xerr=data.xxx, TODO restore
+                yerr=data.npErrors(),
                 label="R",
                 fmt='.',
                 markersize=1.,
@@ -104,7 +79,7 @@ def plot(q, r, exp, P):
 
     ax.set_yscale('log')
 
-    ax.set_xlabel("Q [nm$^{^-1}$]")
+    ax.set_xlabel("q (1/nm)")
     ax.set_ylabel("R")
 
     y = 0.5
@@ -129,6 +104,7 @@ if __name__ == '__main__':
     }
 
     startPnB = {
+        "intensity": (1., 0.8, 1.2),
         "q_offset": (0.01, -0.02, 0.02),
         "q_res/q": (0.01, 0, 0.02),
         "t_pt/nm": (50, 45, 55),
@@ -143,25 +119,28 @@ if __name__ == '__main__':
 
     qmin = 0.18
     qmax = 2.4
-
-    data = load_data("specular/RvsQ_36563_36662.dat.gz", qmin, qmax)
-
     qzs = np.linspace(qmin, qmax, 1500)
 
-    # Plot data with initial model:
+    fpath = os.path.join(datadir, "specular/RvsQ_36563_36662.dat.gz")
+    flags = ba.ImportSettings1D("q (1/angstrom)", "#", "", 1, 2, 3, 4)
+    data = ba.readData1D(fpath, ba.csv1D, flags)
+
+    # Initial plot
 
     r = get_simulation(qzs, initialP | fixedP).simulate().npArray()
     plot(qzs, r, data, initialP)
 
-    # Fit:
+    # Restrict data to given q range
 
-    Q, R, dR = (data[0], data[1], data[2])
+    data = data.crop(qmin, qmax)
+
+    # Fit:
 
     fit_objective = ba.FitObjective()
     fit_objective.setObjectiveMetric("chi2")
     fit_objective.initPrint(10)
-    fit_objective.addSimulationAndData(
-        lambda P: get_simulation(Q, P | fixedP), R, dR, 1)
+    fit_objective.addFitPair(
+        lambda P: get_simulation(data.npXcenters(), P | fixedP), data, 1)
 
     P = ba.Parameters()
     for name, p in startPnB.items():
diff --git a/auto/MiniExamples/specular/PolarizedSpinAsymmetry.py b/auto/MiniExamples/specular/PolarizedSpinAsymmetry.py
index 0123529d57f5d1fbcc58a736a26deb88705e0d01..3185a2719f2c01d1ab3a1ddb56083135a0937efa 100755
--- a/auto/MiniExamples/specular/PolarizedSpinAsymmetry.py
+++ b/auto/MiniExamples/specular/PolarizedSpinAsymmetry.py
@@ -131,7 +131,7 @@ def plotData(qs, rs, exps, labels, colors):
     ax.set_yscale('log')
     plt.legend()
 
-    plt.xlabel("Q [nm${}^{-1}$]")
+    plt.xlabel("q (1/nm)")
     plt.ylabel("R")
 
     plt.tight_layout()
@@ -164,7 +164,7 @@ def plotSpinAsymmetry(data_pp, data_mm, q, r_pp, r_mm):
 
     plt.gca().set_ylim((-0.3, 0.5))
 
-    plt.xlabel("Q [nm${}^{-1}$]")
+    plt.xlabel("Q (1/nm)")
     plt.ylabel("Spin asymmetry")
 
     plt.tight_layout()
diff --git a/rawEx/fit/specular/Honeycomb_fit.py b/rawEx/fit/specular/Honeycomb_fit.py
index 7135c1efc2fb8db6308b18870cea0f0c330716b6..dcb28876ff26f0f90edfd084568a425952362758 100755
--- a/rawEx/fit/specular/Honeycomb_fit.py
+++ b/rawEx/fit/specular/Honeycomb_fit.py
@@ -139,11 +139,11 @@ def plot(q, rs, exps, shifts, labels):
     ax.set_yscale('log')
     plt.legend()
 
-    plt.xlabel("Q [nm${}^{-1}$]")
+    plt.xlabel("q (1/nm)")
     plt.ylabel("R")
 
     <%= sm ? "#" : "" %>plt.tight_layout()
-    <%= sm ? "#" : "" %>plt.close()
+    #<%= sm ? "#" : "" %>plt.close()
 
 
 def plot_sld_profile(P):
@@ -159,12 +159,12 @@ def plot_sld_profile(P):
     plt.plot(z_150p, np.real(sld_150p)*1e6, label=r"150K $+$")
     plt.plot(z_150m, np.real(sld_150m)*1e6, label=r"150K $-$")
 
-    plt.xlabel(r"$z$ [A]")
+    plt.xlabel(r"$z (\AA)$")
     plt.ylabel(r"$\delta(z) \cdot 10^6$")
 
     plt.legend()
     <%= sm ? "#" : "" %>plt.tight_layout()
-    <%= sm ? "#" : "" %>plt.close()
+    #<%= sm ? "#" : "" %>plt.close()
 
 ####################################################################
 #  Main
@@ -181,18 +181,6 @@ if __name__ == '__main__':
         "t_Py2": (56, 46, 66),
         "t_Py1": (56, 46, 66),
         "t_SiO2": (22, 15, 29),
-        "sld_PyOx_real": (1.995, 1.92, 2.07),
-        "sld_Py2_real": (5, 4.7, 5.3),
-        "sld_Py1_real": (4.62, 4.32, 4.92),
-        "rPyOx": (27, 15, 35),
-        "rPy2": (12, 2, 20),
-        "rPy1": (12, 2, 20),
-        "rSiO2": (15, 5, 25),
-        "rSi": (15, 5, 25),
-        "msld_PyOx": (0.25, 0, 1),
-        "msld_Py2": (0.63, 0, 1),
-        "msld_Py1": (0.64, 0, 1),
-        "ms150": (1.05, 1.0, 1.1),
     }
 
     # For fixed parameters, bounds are ignored. We leave them here just
@@ -206,6 +194,19 @@ if __name__ == '__main__':
         "sld_SiO2_real": (3.47, 3, 4),
         "sld_Si_real": (2.0704, 2, 3),
         "dq": (0.018, 0, 0.1),
+    # Start by moving the following back to startPnB:
+        "sld_PyOx_real": (1.995, 1.92, 2.07),
+        "sld_Py2_real": (5, 4.7, 5.3),
+        "sld_Py1_real": (4.62, 4.32, 4.92),
+        "rPyOx": (27, 15, 35),
+        "rPy2": (12, 2, 20),
+        "rPy1": (12, 2, 20),
+        "rSiO2": (15, 5, 25),
+        "rSi": (15, 5, 25),
+        "msld_PyOx": (0.25, 0, 1),
+        "msld_Py2": (0.63, 0, 1),
+        "msld_Py1": (0.64, 0, 1),
+        "ms150": (1.05, 1.0, 1.1),
     }
 
     fixedP = {d: v[0] for d, v in fixedPnB.items()}
@@ -261,8 +262,8 @@ if __name__ == '__main__':
     result = scipy.optimize.differential_evolution(
         objective_function,
         bounds,
-        maxiter=<%= sm ? 2 : 500 %>,
-        popsize=<%= sm ? 2 : 10 %>,
+        maxiter=<%= sm ? 2 : 5 %>, # for a serious DE fit, choose 500
+        popsize=<%= sm ? 2 : 3 %>, # for a serious DE fit, choose 10
         tol=1e-2,
         mutation=(0.5, 1.5),
         seed=0,
diff --git a/rawEx/fit/specular/Pt_layer_fit.py b/rawEx/fit/specular/Pt_layer_fit.py
index 0d0b68d43839ac197d3c39e6b4e24ecd82905332..cc9200f315465156efe249e1e99079881dac4730 100755
--- a/rawEx/fit/specular/Pt_layer_fit.py
+++ b/rawEx/fit/specular/Pt_layer_fit.py
@@ -47,6 +47,7 @@ def get_simulation(q_axis, P):
     sample = get_sample(P)
 
     scan = ba.QzScan(q_axis)
+    scan.setIntensity(P["intensity"])
     scan.setOffset(P["q_offset"])
 
     distr = ba.DistributionGaussian(0., 1., 25, 4.)
@@ -56,44 +57,18 @@ def get_simulation(q_axis, P):
 
     return simulation
 
-####################################################################
-#  Experimental data
-####################################################################
-
-def load_data(filename, qmin, qmax):
-    filepath = os.path.join(datadir, filename)
-    data = np.genfromtxt(filepath, unpack=True)
-
-    r0 = np.where(data[0] - np.roll(data[0], 1) == 0)
-    data = np.delete(data, r0, 1)
-
-    data[0] = data[0]/angstrom
-    data[3] = data[3]/angstrom
-
-    data[1] = data[1]
-    data[2] = data[2]
-
-    so = np.argsort(data[0])
-
-    data = data[:, so]
-
-    minIndex = np.argmin(np.abs(data[0] - qmin))
-    maxIndex = np.argmin(np.abs(data[0] - qmax))
-
-    return data[:, minIndex:maxIndex + 1]
-
 ####################################################################
 #  Plotting
 ####################################################################
 
-def plot(q, r, exp, P):
+def plot(q, r, data, P):
     fig = plt.figure()
     ax = fig.add_subplot(111)
 
-    ax.errorbar(exp[0],
-                exp[1],
-                xerr=exp[3],
-                yerr=exp[2],
+    ax.errorbar(data.npXcenters(),
+                data.npArray(),
+                # xerr=data.xxx, TODO restore
+                yerr=data.npErrors(),
                 label="R",
                 fmt='.',
                 markersize=1.,
@@ -104,7 +79,7 @@ def plot(q, r, exp, P):
 
     ax.set_yscale('log')
 
-    ax.set_xlabel("Q [nm$^{^-1}$]")
+    ax.set_xlabel("q (1/nm)")
     ax.set_ylabel("R")
 
     y = 0.5
@@ -129,6 +104,7 @@ if __name__ == '__main__':
     }
 
     startPnB = {
+        "intensity": (1., 0.8, 1.2),
         "q_offset": (0.01, -0.02, 0.02),
         "q_res/q": (0.01, 0, 0.02),
         "t_pt/nm": (50, 45, 55),
@@ -143,25 +119,28 @@ if __name__ == '__main__':
 
     qmin = 0.18
     qmax = 2.4
-
-    data = load_data("specular/RvsQ_36563_36662.dat.gz", qmin, qmax)
-
     qzs = np.linspace(qmin, qmax, 1500)
 
-    # Plot data with initial model:
+    fpath = os.path.join(datadir, "specular/RvsQ_36563_36662.dat.gz")
+    flags = ba.ImportSettings1D("q (1/angstrom)", "#", "", 1, 2, 3, 4)
+    data = ba.readData1D(fpath, ba.csv1D, flags)
+
+    # Initial plot
 
     r = get_simulation(qzs, initialP | fixedP).simulate().npArray()
     plot(qzs, r, data, initialP)
 
-    # Fit:
+    # Restrict data to given q range
 
-    Q, R, dR = (data[0], data[1], data[2])
+    data = data.crop(qmin, qmax)
+
+    # Fit:
 
     fit_objective = ba.FitObjective()
     fit_objective.setObjectiveMetric("chi2")
     fit_objective.initPrint(10)
-    fit_objective.addSimulationAndData(
-        lambda P: get_simulation(Q, P | fixedP), R, dR, 1)
+    fit_objective.addFitPair(
+        lambda P: get_simulation(data.npXcenters(), P | fixedP), data, 1)
 
     P = ba.Parameters()
     for name, p in startPnB.items():
diff --git a/rawEx/specular/PolarizedSpinAsymmetry.py b/rawEx/specular/PolarizedSpinAsymmetry.py
index 9378c689fd107345d13dfeaf1b09918ce3625511..cba1b8ac600aa6455fbf3a1639fd6b77212d787f 100755
--- a/rawEx/specular/PolarizedSpinAsymmetry.py
+++ b/rawEx/specular/PolarizedSpinAsymmetry.py
@@ -131,7 +131,7 @@ def plotData(qs, rs, exps, labels, colors):
     ax.set_yscale('log')
     plt.legend()
 
-    plt.xlabel("Q [nm${}^{-1}$]")
+    plt.xlabel("q (1/nm)")
     plt.ylabel("R")
 
     plt.tight_layout()
@@ -164,7 +164,7 @@ def plotSpinAsymmetry(data_pp, data_mm, q, r_pp, r_mm):
 
     plt.gca().set_ylim((-0.3, 0.5))
 
-    plt.xlabel("Q [nm${}^{-1}$]")
+    plt.xlabel("Q (1/nm)")
     plt.ylabel("Spin asymmetry")
 
     plt.tight_layout()