diff --git a/refnx/reflect/spline.py b/refnx/reflect/spline.py
index 6d7cd091..0a542992 100644
--- a/refnx/reflect/spline.py
+++ b/refnx/reflect/spline.py
@@ -15,7 +15,7 @@ class Spline(Component):
     """
 
     def __init__(self, extent, vs, dz, left, right, solvent, name='',
-                 interpolator=Pchip, zgrad=True, microslab_max_thickness=2):
+                 interpolator=Pchip, zgrad=True, microslab_max_thickness=1):
         """
         Parameters
         ----------
@@ -65,9 +65,9 @@ class Spline(Component):
         microslab is `microslab_max_thickness`.
         """
         self.name = name
-        self.left_Slab = left
-        self.right_Slab = right
-        self.solvent_Slab = solvent
+        self.left_slab = left
+        self.right_slab = right
+        self.solvent_slab = solvent
         self.microslab_max_thickness = microslab_max_thickness
 
         self.extent = (
@@ -95,45 +95,87 @@ class Spline(Component):
         self.zgrad = zgrad
         self.interpolator = interpolator
 
-    def __call__(self, z):
-        # calculate spline value at z
-        zeds = np.cumsum(self.dz)
+        self.__cached_interpolator = {'zeds': np.array([]),
+                                      'vs': np.array([]),
+                                      'interp': None,
+                                      'extent': -1}
+
+    def _interpolator(self):
+        dz = np.array(self.dz)
+        zeds = np.cumsum(dz)
 
         # if dz's sum to more than 1, then normalise to unit interval.
-        if np.sum(self.dz) > 1:
-            zeds /= np.sum(self.dz)
+        if zeds[-1] > 1:
+            zeds /= zeds[-1]
+            zeds = np.clip(zeds, 0, 1)
 
         vs = np.array(self.vs)
 
         left_sld = Structure.overall_sld(
-            np.atleast_2d(self.left_Slab.slabs[-1]),
-            self.solvent_Slab.slabs)[..., 1]
+            np.atleast_2d(self.left_slab.slabs[-1]),
+            self.solvent_slab.slabs)[..., 1]
 
         right_sld = Structure.overall_sld(
-            np.atleast_2d(self.right_Slab.slabs[0]),
-            self.solvent_Slab.slabs)[..., 1]
+            np.atleast_2d(self.right_slab.slabs[0]),
+            self.solvent_slab.slabs)[..., 1]
 
         if self.zgrad:
-            zeds = np.r_[-1.1, 0, zeds, 1, 2.1]
-            vs = np.r_[left_sld, left_sld, vs, right_sld, right_sld]
+            zeds = np.concatenate([[-1.1, 0], zeds, [1, 2.1]])
+            print(left_sld.shape, right_sld.shape)
+            vs = np.concatenate([left_sld, left_sld, vs, right_sld, right_sld])
+        else:
+            zeds = np.concatenate([[0], zeds, [1]])
+            vs = np.concatenate([left_sld, vs, right_sld])
+
+        # cache the interpolator
+        cache_zeds = self.__cached_interpolator['zeds']
+        cache_vs = self.__cached_interpolator['vs']
+        cache_extent = self.__cached_interpolator['extent']
+
+        # you don't need to recreate the interpolator
+        if (np.array_equal(zeds, cache_zeds) and
+                np.array_equal(vs, cache_vs) and
+                np.equal(self.extent, cache_extent)):
+            return self.__cached_interpolator['interp']
         else:
-            zeds = np.r_[0, zeds, 1]
-            vs = np.r_[left_sld, vs, right_sld]
-        return self.interpolator(zeds, vs)(z / float(self.extent))
+            self.__cached_interpolator['zeds'] = zeds
+            self.__cached_interpolator['vs'] = vs
+            self.__cached_interpolator['extent'] = float(self.extent)
+
+        # TODO make vfp zero for z > self.extent
+        interpolator = self.interpolator(zeds, vs)
+        self.__cached_interpolator['interp'] = interpolator
+        return interpolator
+
+    def __call__(self, z):
+        """
+        Calculates the spline value at z
+
+        Parameters
+        ----------
+        z : float
+            Distance along spline
+
+        Returns
+        -------
+        sld : float
+            Real part of SLD
+        """
+        interpolator = self._interpolator()
+        vs = interpolator(z / float(self.extent))
+        return vs
 
     @property
     def parameters(self):
         p = Parameters(name=self.name)
         p.extend([self.extent, self.dz, self.vs,
-                  self.left_Slab,
-                  self.right_Slab,
-                  self.solvent_Slab])
+                  self.left_slab.parameters,
+                  self.right_slab.parameters,
+                  self.solvent_slab.parameters])
         return p
 
     def lnprob(self):
-        zeds = np.cumsum(self.dz)
-        if zeds[-1] > 1:
-            return -np.inf
+        return 0
 
     @property
     def slabs(self):
@@ -142,8 +184,8 @@ class Spline(Component):
         slabs = np.zeros((int(num_slabs), 5))
         slabs[:, 0] = slab_thick
 
-        # give each slab a miniscule roughness
-        slabs[:, 3] = 0.5
+        # give last slab a miniscule roughness so it doesn't get contracted
+        slabs[-1:, 3] = 0.5
 
         dist = np.cumsum(slabs[..., 0]) - 0.5 * slab_thick
         slabs[:, 1] = self(dist)

reflect.spline issues
lnprob doesn't return 0
parameters needs to return list of parameters
remove minimal roughness
speedup like brush code.
diff --git a/refnx/reflect/test/test_spline.py b/refnx/reflect/test/test_spline.py
index 36a9634b..cafd086e 100644
--- a/refnx/reflect/test/test_spline.py
+++ b/refnx/reflect/test/test_spline.py
@@ -1,6 +1,9 @@
 import numpy as np
-from numpy.testing import assert_allclose, assert_equal, assert_almost_equal
+from numpy.testing import (assert_allclose, assert_equal, assert_almost_equal,
+                           assert_)
 from refnx.reflect import SLD, Slab, Structure, Spline
+from refnx.analysis import Parameter
+from refnx._lib import flatten
 
 
 class TestReflect(object):
@@ -14,10 +17,9 @@ class TestReflect(object):
         # smoke test to make Spline at least gives us something
         a = Spline(100, [2],
                    [0.5], self.left, self.right, self.solvent, zgrad=False,
-                   microslab_max_thickness=2)
+                   microslab_max_thickness=1)
         b = a.slabs
         assert_equal(b[:, 2], 0)
-        assert_equal(b[:, 3], 0.5)
 
         # microslabs are assessed in the middle of the slab
         assert_equal(b[0, 1], a(0.5 * b[0, 0]))
@@ -25,12 +27,24 @@ class TestReflect(object):
         # with the ends turned off the profile should be a straight line
         assert_equal(a(50), 2.0)
 
+        # construct a structure
+        a = Spline(100, [2., 3., 4.],
+                   [0.25] * 3, self.left, self.right, self.solvent,
+                   zgrad=False, microslab_max_thickness=1)
+
+        s = self.left | a | self.right | self.solvent
+        # calculate an SLD profile
+        s.sld_profile()
+        # ask for the parameters
+        for p in flatten(s.parameters):
+            assert_(isinstance(p, Parameter))
+
     def test_left_right_influence(self):
         # make sure that if the left and right components change, so does the
         # spline
         a = Spline(100, [2],
                    [0.5], self.left, self.right, self.solvent, zgrad=False,
-                   microslab_max_thickness=2)
+                   microslab_max_thickness=1)
 
         # change the SLD of the left component, spline should respond
         self.left.sld.real.value = 2.

pip install -e .[dev]
parse_log_pytest
numpy>=1.16.0 scipy>=1.0.0 emcee>=2.2.1 six>=1.11.0 uncertainties>=3.0.1 pandas>=0.23.4 pytest>=3.6.0 h5py>=2.8.0 xlrd>=1.1.0 ptemcee>=1.0.0
pytest
apt-get update apt-get install -y gcc
3.6
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs @ file:///opt/conda/conda-bld/attrs_1642510447205/work
certifi==2021.5.30
emcee @ file:///home/conda/feedstock_root/build_artifacts/emcee_1713796893786/work
future==0.18.2
h5py @ file:///tmp/build/80754af9/h5py_1593454121459/work
importlib-metadata @ file:///tmp/build/80754af9/importlib-metadata_1631916693255/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
more-itertools @ file:///tmp/build/80754af9/more-itertools_1637733554872/work
numpy @ file:///tmp/build/80754af9/numpy_and_numpy_base_1603483703303/work
packaging @ file:///tmp/build/80754af9/packaging_1637314298585/work
pandas==1.1.5
pluggy @ file:///tmp/build/80754af9/pluggy_1615976315926/work
ptemcee==1.0.0
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pyparsing @ file:///tmp/build/80754af9/pyparsing_1635766073266/work
pytest==6.2.4
python-dateutil @ file:///tmp/build/80754af9/python-dateutil_1626374649649/work
pytz==2021.3
-e git+https://github.com/refnx/refnx.git@568a56132fe0cd8418cff41ffedfc276bdb99af4#egg=refnx
scipy @ file:///tmp/build/80754af9/scipy_1597686635649/work
six @ file:///tmp/build/80754af9/six_1644875935023/work
toml @ file:///tmp/build/80754af9/toml_1616166611790/work
typing_extensions @ file:///opt/conda/conda-bld/typing_extensions_1647553014482/work
uncertainties @ file:///home/conda/feedstock_root/build_artifacts/uncertainties_1720452225073/work
xlrd @ file:///croot/xlrd_1685030938141/work
zipp @ file:///tmp/build/80754af9/zipp_1633618647012/work

name: refnx
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=21.4.0=pyhd3eb1b0_0
  - blas=1.0=openblas
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - emcee=3.1.6=pyhd8ed1ab_0
  - future=0.18.2=py36_1
  - h5py=2.10.0=py36hd6299e0_1
  - hdf5=1.10.6=hb1b8bf9_0
  - importlib-metadata=4.8.1=py36h06a4308_0
  - importlib_metadata=4.8.1=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgfortran-ng=7.5.0=ha8ba4b0_17
  - libgfortran4=7.5.0=ha8ba4b0_17
  - libgomp=11.2.0=h1234567_1
  - libopenblas=0.3.18=hf726d26_0
  - libstdcxx-ng=11.2.0=h1234567_1
  - more-itertools=8.12.0=pyhd3eb1b0_0
  - ncurses=6.4=h6a678d5_0
  - numpy=1.19.2=py36h6163131_0
  - numpy-base=1.19.2=py36h75fe3a5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=21.3=pyhd3eb1b0_0
  - pandas=1.1.5=py36ha9443f7_0
  - pip=21.2.2=py36h06a4308_0
  - pluggy=0.13.1=py36h06a4308_0
  - ptemcee=1.0.0=py_0
  - py=1.11.0=pyhd3eb1b0_0
  - pyparsing=3.0.4=pyhd3eb1b0_0
  - pytest=6.2.4=py36h06a4308_2
  - python=3.6.13=h12debd9_1
  - python-dateutil=2.8.2=pyhd3eb1b0_0
  - pytz=2021.3=pyhd3eb1b0_0
  - readline=8.2=h5eee18b_0
  - scipy=1.5.2=py36habc2bb6_0
  - setuptools=58.0.4=py36h06a4308_0
  - six=1.16.0=pyhd3eb1b0_1
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - toml=0.10.2=pyhd3eb1b0_0
  - typing_extensions=4.1.1=pyh06a4308_0
  - uncertainties=3.2.2=pyhd8ed1ab_1
  - wheel=0.37.1=pyhd3eb1b0_0
  - xlrd=2.0.1=pyhd3eb1b0_1
  - xz=5.6.4=h5eee18b_1
  - zipp=3.6.0=pyhd3eb1b0_0
  - zlib=1.2.13=h5eee18b_1
prefix: /opt/conda/envs/refnx

refnx/reflect/test/test_spline.py::TestReflect::test_spline_smoke
refnx/reflect/test/test_spline.py::TestReflect::test_left_right_influence
refnx/reflect/spline.py
refnx/reflect/spline.py