diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9c6d0878810b7d8955aeb118e9a0286f27fbb1b0..d1572e049c48385afb8d9a75148da796cffb07a8 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -43,7 +43,7 @@ basic-3.7:
 # test example on python 3.7
 python-3.7:
   stage: test
-  image: bilbydev/v2-dockerfile-test-suite-python37
+  image: quay.io/bilbydev/v2-dockerfile-test-suite-python37
   script:
     - python -m pip install .
 
@@ -69,7 +69,7 @@ python-3.7:
 # test example on python 3.8
 python-3.8:
   stage: test
-  image: bilbydev/v2-dockerfile-test-suite-python38
+  image: quay.io/bilbydev/v2-dockerfile-test-suite-python38
   script:
     - python -m pip install .
 
@@ -78,7 +78,7 @@ python-3.8:
 # test example on python 3.6
 python-3.6:
   stage: test
-  image: bilbydev/v2-dockerfile-test-suite-python36
+  image: quay.io/bilbydev/v2-dockerfile-test-suite-python36
   script:
     - python -m pip install .
 
@@ -87,7 +87,7 @@ python-3.6:
 # test samplers on python 3.7
 python-3.7-samplers:
   stage: test
-  image: bilbydev/v2-dockerfile-test-suite-python37
+  image: quay.io/bilbydev/v2-dockerfile-test-suite-python37
   script:
     - python -m pip install .
 
@@ -97,7 +97,7 @@ python-3.7-samplers:
 # test samplers on python 3.6
 python-3.6-samplers:
   stage: test
-  image: bilbydev/v2-dockerfile-test-suite-python36
+  image: quay.io/bilbydev/v2-dockerfile-test-suite-python36
   script:
     - python -m pip install .
 
@@ -106,7 +106,7 @@ python-3.6-samplers:
 # Test containers are up to date
 containers:
   stage: test
-  image: bilbydev/v2-dockerfile-test-suite-python37
+  image: quay.io/bilbydev/v2-dockerfile-test-suite-python37
   script:
     - cd containers
     - python write_dockerfiles.py
@@ -117,7 +117,7 @@ containers:
 # Tests run at a fixed schedule rather than on push
 scheduled-python-3.7:
   stage: test
-  image: bilbydev/v2-dockerfile-test-suite-python37
+  image: quay.io/bilbydev/v2-dockerfile-test-suite-python37
   only:
     - schedules
   script:
@@ -129,7 +129,7 @@ scheduled-python-3.7:
 
 plotting:
   stage: test
-  image: bilbydev/bilby-test-suite-python37
+  image: quay.io/bilbydev/v2-dockerfile-test-suite-python37
   only:
     - schedules
   script:
@@ -140,7 +140,7 @@ plotting:
 
 authors:
   stage: test
-  image: bilbydev/bilby-test-suite-python37
+  image: quay.io/bilbydev/v2-dockerfile-test-suite-python37
   script:
     - python test/check_author_list.py
 
@@ -162,7 +162,7 @@ pages:
 
 deploy_release:
   stage: deploy
-  image: bilbydev/v2-dockerfile-test-suite-python37
+  image: quay.io/bilbydev/v2-dockerfile-test-suite-python37
   variables:
     TWINE_USERNAME: $PYPI_USERNAME
     TWINE_PASSWORD: $PYPI_PASSWORD
@@ -177,7 +177,7 @@ deploy_release:
 
 precommits-py3.7:
   stage: test
-  image: bilbydev/v2-dockerfile-test-suite-python37
+  image: quay.io/bilbydev/v2-dockerfile-test-suite-python37
   script:
     - source activate python37
     - mkdir -p .pip37
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 612437929298e18624c5d170b790075909a6e7c6..ddbd703b36f04a438a61bcc14338310d13c0ef43 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
 # All notable changes will be documented in this file
 
+## [1.0.3] 2020-11-23
+Version 1.0.4 release of bilby
+
+### Added
+- Added a chirp-mass and mass-ratio prior which are uniform in component masses (!891)
+
+### Changes
+- Fixed issue in the CI
+
 ## [1.0.3] 2020-10-23
 
 Version 1.0.3 release of bilby
diff --git a/bilby/core/prior/analytical.py b/bilby/core/prior/analytical.py
index 7a7fb2b6b5c59b5be7178e60172c889a0b20d380..a2aa4cac57a9ea2c44f0768fc697bdbca5ef520a 100644
--- a/bilby/core/prior/analytical.py
+++ b/bilby/core/prior/analytical.py
@@ -370,8 +370,8 @@ class SymmetricLogUniform(Prior):
 
 class Cosine(Prior):
 
-    def __init__(self, name=None, latex_label=None, unit=None,
-                 minimum=-np.pi / 2, maximum=np.pi / 2, boundary=None):
+    def __init__(self, minimum=-np.pi / 2, maximum=np.pi / 2, name=None,
+                 latex_label=None, unit=None, boundary=None):
         """Cosine prior with bounds
 
         Parameters
@@ -389,8 +389,8 @@ class Cosine(Prior):
         boundary: str
             See superclass
         """
-        super(Cosine, self).__init__(name=name, latex_label=latex_label, unit=unit,
-                                     minimum=minimum, maximum=maximum, boundary=boundary)
+        super(Cosine, self).__init__(minimum=minimum, maximum=maximum, name=name,
+                                     latex_label=latex_label, unit=unit, boundary=boundary)
 
     def rescale(self, val):
         """
@@ -425,8 +425,8 @@ class Cosine(Prior):
 
 class Sine(Prior):
 
-    def __init__(self, name=None, latex_label=None, unit=None, minimum=0,
-                 maximum=np.pi, boundary=None):
+    def __init__(self, minimum=0, maximum=np.pi, name=None,
+                 latex_label=None, unit=None, boundary=None):
         """Sine prior with bounds
 
         Parameters
@@ -444,8 +444,8 @@ class Sine(Prior):
         boundary: str
             See superclass
         """
-        super(Sine, self).__init__(name=name, latex_label=latex_label, unit=unit,
-                                   minimum=minimum, maximum=maximum, boundary=boundary)
+        super(Sine, self).__init__(minimum=minimum, maximum=maximum, name=name,
+                                   latex_label=latex_label, unit=unit, boundary=boundary)
 
     def rescale(self, val):
         """
diff --git a/bilby/core/sampler/dynesty.py b/bilby/core/sampler/dynesty.py
index e1b0dd1f2831e60df867905709e317def3b425c2..206ed9a42ccb28af6a251e8a0dfa9f7c20fb753a 100644
--- a/bilby/core/sampler/dynesty.py
+++ b/bilby/core/sampler/dynesty.py
@@ -612,7 +612,7 @@ class Dynesty(NestedSampler):
                 fig = dyplot.traceplot(self.sampler.results, labels=labels)[0]
                 fig.tight_layout()
                 fig.savefig(filename)
-            except (RuntimeError, np.linalg.linalg.LinAlgError, ValueError) as e:
+            except (RuntimeError, np.linalg.linalg.LinAlgError, ValueError, OverflowError, Exception) as e:
                 logger.warning(e)
                 logger.warning('Failed to create dynesty state plot at checkpoint')
             finally:
@@ -690,6 +690,16 @@ class Dynesty(NestedSampler):
         """
         return self.priors.rescale(self._search_parameter_keys, theta)
 
+    def calc_likelihood_count(self):
+        if self.likelihood_benchmark:
+            if hasattr(self, 'sampler'):
+                self.result.num_likelihood_evaluations = \
+                    getattr(self.sampler, 'ncall', 0)
+            else:
+                self.result.num_likelihood_evaluations = 0
+        else:
+            return None
+
 
 def sample_rwalk_bilby(args):
     """ Modified bilby-implemented version of dynesty.sampling.sample_rwalk """
diff --git a/bilby/core/sampler/ultranest.py b/bilby/core/sampler/ultranest.py
index fdae9a8e9c4393b857a58a27a8f075d2bc704d12..9fa12578ddb9154d5ccdab5101c482891d83fa6d 100644
--- a/bilby/core/sampler/ultranest.py
+++ b/bilby/core/sampler/ultranest.py
@@ -61,7 +61,7 @@ class Ultranest(NestedSampler):
         log_interval=None,
         dlogz=None,
         max_iters=None,
-        update_interval_iter_fraction=0.2,
+        update_interval_volume_fraction=0.2,
         viz_callback=None,
         dKL=0.5,
         frac_remain=0.01,
@@ -232,7 +232,7 @@ class Ultranest(NestedSampler):
             ]
         else:
             keys = [
-                "update_interval_iter_fraction",
+                "update_interval_volume_fraction",
                 "update_interval_ncall",
                 "log_interval",
                 "show_status",
diff --git a/bilby/gw/detector/detectors/A1.interferometer b/bilby/gw/detector/detectors/A1.interferometer
new file mode 100644
index 0000000000000000000000000000000000000000..2cfb564a3b36b4aa3e41d7e6623b8b74e98264cf
--- /dev/null
+++ b/bilby/gw/detector/detectors/A1.interferometer
@@ -0,0 +1,13 @@
+# LIGO India Aundha at Aplus sensitvity.
+# LIGO-T2000158
+# https://dcc.ligo.org/LIGO-T2000012/public
+name = 'A1'
+power_spectral_density = PowerSpectralDensity(asd_file='Aplus_asd.txt')
+minimum_frequency = 20
+maximum_frequency = 2048
+length = 4
+latitude = 19 + 36. / 60 + 47.9017 / 3600
+longitude = 77 + 1. / 60 + 51.0997 / 3600
+elevation = 440.0
+xarm_azimuth = 117.6157
+yarm_azimuth = 207.6165
diff --git a/bilby/gw/detector/strain_data.py b/bilby/gw/detector/strain_data.py
index 6ba6db039ef244e5bb3295ba8796347af868a60f..da0a397827c3892316962513cc1a1da3e9ef2d6d 100644
--- a/bilby/gw/detector/strain_data.py
+++ b/bilby/gw/detector/strain_data.py
@@ -31,7 +31,7 @@ class InterferometerStrainData(object):
     time_array = PropertyAccessor('_times_and_frequencies', 'time_array')
 
     def __init__(self, minimum_frequency=0, maximum_frequency=np.inf,
-                 roll_off=0.2):
+                 roll_off=0.2, notch_list=None):
         """ Initiate an InterferometerStrainData object
 
         The initialised object contains no data, this should be added using one
@@ -46,11 +46,14 @@ class InterferometerStrainData(object):
         roll_off: float
             The roll-off (in seconds) used in the Tukey window, default=0.2s.
             This corresponds to alpha * duration / 2 for scipy tukey window.
+        notch_list: bilby.gw.detector.strain_data.NotchList
+            A list of notches
 
         """
 
         self.minimum_frequency = minimum_frequency
         self.maximum_frequency = maximum_frequency
+        self.notch_list = notch_list
         self.roll_off = roll_off
         self.window_factor = 1
 
@@ -122,18 +125,46 @@ class InterferometerStrainData(object):
         self._maximum_frequency = maximum_frequency
         self._frequency_mask_updated = False
 
+    @property
+    def notch_list(self):
+        return self._notch_list
+
+    @notch_list.setter
+    def notch_list(self, notch_list):
+        """ Set the notch_list
+
+        Parameters
+        ----------
+        notch_list: list, bilby.gw.detector.strain_data.NotchList
+            A list of length-2 tuples of the (max, min) frequency for the
+            notches or a pre-made bilby NotchList.
+
+        """
+        if notch_list is None:
+            self._notch_list = NotchList(None)
+        elif isinstance(notch_list, list):
+            self._notch_list = NotchList(notch_list)
+        elif isinstance(notch_list, NotchList):
+            self._notch_list = notch_list
+        else:
+            raise ValueError("notch_list {} not understood".format(notch_list))
+        self._frequency_mask_updated = False
+
     @property
     def frequency_mask(self):
-        """Masking array for limiting the frequency band.
+        """ Masking array for limiting the frequency band.
 
         Returns
         -------
-        array_like: An array of boolean values
+        mask: np.ndarray
+            An array of boolean values
         """
         if not self._frequency_mask_updated:
             frequency_array = self._times_and_frequencies.frequency_array
             mask = ((frequency_array >= self.minimum_frequency) &
                     (frequency_array <= self.maximum_frequency))
+            for notch in self.notch_list:
+                mask[notch.get_idxs(frequency_array)] = False
             self._frequency_mask = mask
             self._frequency_mask_updated = True
         return self._frequency_mask
@@ -683,3 +714,104 @@ class InterferometerStrainData(object):
         strain = strain.resample(sampling_frequency)
 
         self.set_from_gwpy_timeseries(strain)
+
+
+class Notch(object):
+    def __init__(self, minimum_frequency, maximum_frequency):
+        """ A notch object storing the maximum and minimum frequency of the notch
+
+        Parameters
+        ----------
+        minimum_frequency, maximum_frequency: float
+            The minimum and maximum frequency of the notch
+
+        """
+
+        if 0 < minimum_frequency < maximum_frequency < np.inf:
+            self.minimum_frequency = minimum_frequency
+            self.maximum_frequency = maximum_frequency
+        else:
+            msg = ("Your notch minimum_frequency {} and maximum_frequency {} are invalid"
+                   .format(minimum_frequency, maximum_frequency))
+            raise ValueError(msg)
+
+    def get_idxs(self, frequency_array):
+        """ Get a boolean mask for the frequencies in frequency_array in the notch
+
+        Parameters
+        ----------
+        frequency_array: np.ndarray
+            An array of frequencies
+
+        Returns
+        -------
+        idxs: np.ndarray
+            An array of booleans which are True for frequencies in the notch
+
+        """
+        lower = (frequency_array > self.minimum_frequency)
+        upper = (frequency_array < self.maximum_frequency)
+        return lower & upper
+
+    def check_frequency(self, freq):
+        """ Check if freq is inside the notch
+
+        Parameters
+        ----------
+        freq: float
+            The frequency to check
+
+        Returns
+        -------
+        True/False:
+            If freq inside the notch, return True, else False
+        """
+
+        if self.minimum_frequency < freq < self.maximum_frequency:
+            return True
+        else:
+            return False
+
+
+class NotchList(list):
+    def __init__(self, notch_list):
+        """ A list of notches
+
+        Parameters
+        ----------
+        notch_list: list
+            A list of length-2 tuples of the (max, min) frequency for the
+            notches.
+
+        Raises
+        ------
+        ValueError
+            If the list is malformed.
+        """
+
+        if notch_list is not None:
+            for notch in notch_list:
+                if isinstance(notch, tuple) and len(notch) == 2:
+                    self.append(Notch(*notch))
+                else:
+                    msg = "notch_list {} is malformed".format(notch_list)
+                    raise ValueError(msg)
+
+    def check_frequency(self, freq):
+        """ Check if freq is inside the notch list
+
+        Parameters
+        ----------
+        freq: float
+            The frequency to check
+
+        Returns
+        -------
+        True/False:
+            If freq inside any of the notches, return True, else False
+        """
+
+        for notch in self:
+            if notch.check_frequency(freq):
+                return True
+        return False
diff --git a/bilby/gw/prior.py b/bilby/gw/prior.py
index 7d1b37da3bf3c0f35000fc699f6c48cd85aa772e..3ffcfb193df6f34ed07deb9689def2adf444bb60 100644
--- a/bilby/gw/prior.py
+++ b/bilby/gw/prior.py
@@ -4,11 +4,13 @@ import copy
 import numpy as np
 from scipy.interpolate import InterpolatedUnivariateSpline, interp1d
 from scipy.integrate import cumtrapz
+from scipy.special import hyp2f1
 from scipy.stats import norm
 
 from ..core.prior import (PriorDict, Uniform, Prior, DeltaFunction, Gaussian,
                           Interped, Constraint, conditional_prior_factory,
-                          BaseJointPriorDist, JointPrior, JointPriorDistError)
+                          BaseJointPriorDist, JointPrior, JointPriorDistError,
+                          PowerLaw)
 from ..core.utils import infer_args_from_method, logger
 from .conversion import (
     convert_to_lal_binary_black_hole_parameters,
@@ -285,6 +287,93 @@ class UniformSourceFrame(Cosmological):
         return zs, p_dz
 
 
+class UniformInComponentsChirpMass(PowerLaw):
+
+    def __init__(self, minimum, maximum, name='chirp_mass',
+                 latex_label='$\mathcal{M}$', unit=None, boundary=None):
+        """
+        Prior distribution for chirp mass which is uniform in component masses.
+
+        This is useful when chirp mass and mass ratio are sampled while the
+        prior is uniform in component masses.
+
+        Parameters
+        ----------
+        minimum : float
+            The minimum of chirp mass
+        maximum : float
+            The maximum of chirp mass
+        name: see superclass
+        latex_label: see superclass
+        unit: see superclass
+        boundary: see superclass
+        """
+        super(UniformInComponentsChirpMass, self).__init__(
+            alpha=1., minimum=minimum, maximum=maximum,
+            name=name, latex_label=latex_label, unit=unit, boundary=boundary)
+
+
+class WrappedInterp1d(interp1d):
+    """ A wrapper around scipy interp1d which sets equality-by-instantiation """
+    def __eq__(self, other):
+
+        for key in self.__dict__:
+            if type(self.__dict__[key]) is np.ndarray:
+                if not np.array_equal(self.__dict__[key], other.__dict__[key]):
+                    return False
+            elif key == "_spline":
+                pass
+            elif getattr(self, key) != getattr(other, key):
+                return False
+        return True
+
+
+class UniformInComponentsMassRatio(Prior):
+
+    def __init__(self, minimum, maximum, name='mass_ratio', latex_label='$q$',
+                 unit=None, boundary=None):
+        """
+        Prior distribution for mass ratio which is uniform in component masses.
+
+        This is useful when chirp mass and mass ratio are sampled while the
+        prior is uniform in component masses.
+
+        Parameters
+        ----------
+        minimum : float
+            The minimum of mass ratio
+        maximum : float
+            The maximum of mass ratio
+        name: see superclass
+        latex_label: see superclass
+        unit: see superclass
+        boundary: see superclass
+        """
+        super(UniformInComponentsMassRatio, self).__init__(
+            minimum=minimum, maximum=maximum, name=name,
+            latex_label=latex_label, unit=unit, boundary=boundary)
+        self.norm = self._integral(maximum) - self._integral(minimum)
+        qs = np.linspace(minimum, maximum, 1000)
+        self.icdf = WrappedInterp1d(
+            self.cdf(qs), qs, kind='cubic',
+            bounds_error=False, fill_value=(minimum, maximum))
+
+    @staticmethod
+    def _integral(q):
+        return -5. * q**(-1. / 5.) * hyp2f1(-2. / 5., -1. / 5., 4. / 5., -q)
+
+    def cdf(self, val):
+        return (self._integral(val) - self._integral(self.minimum)) / self.norm
+
+    def rescale(self, val):
+        self.test_valid_for_rescaling(val)
+        return self.icdf(val)
+
+    def prob(self, val):
+        in_prior = (val >= self.minimum) & (val <= self.maximum)
+        return (1. + val)**(2. / 5.) / (val**(6. / 5.)) / self.norm * in_prior
+
+
 class AlignedSpin(Interped):
 
     def __init__(self, a_prior=Uniform(0, 1), z_prior=Uniform(-1, 1),
diff --git a/bilby/gw/source.py b/bilby/gw/source.py
index 69109364a149304926318ebd8b73fc1adf96425e..3a346725d74c4a8ddf669bc1ec612c6acb87b75f 100644
--- a/bilby/gw/source.py
+++ b/bilby/gw/source.py
@@ -343,6 +343,15 @@ def _base_lal_cbc_fd_waveform(
     lalsim_SimInspiralWaveformParamsInsertTidalLambda2(
         waveform_dictionary, lambda_2)
 
+    for key, value in waveform_kwargs.items():
+        func = getattr(lalsim, "SimInspiralWaveformParamsInsert" + key, None)
+        if func is not None:
+            func(waveform_dictionary, value)
+
+    if waveform_kwargs.get('numerical_relativity_file', None) is not None:
+        lalsim.SimInspiralWaveformParamsInsertNumRelData(
+            waveform_dictionary, waveform_kwargs['numerical_relativity_file'])
+
     if ('mode_array' in waveform_kwargs) and waveform_kwargs['mode_array'] is not None:
         mode_array = waveform_kwargs['mode_array']
         mode_array_lal = lalsim.SimInspiralCreateModeArray()
diff --git a/containers/dockerfile-template b/containers/dockerfile-template
index 46d4f54700cb9bfad245e7d1e3113b85589d73f4..eb2fa024cae7f1f93bdfa0d6ef256d6867538809 100644
--- a/containers/dockerfile-template
+++ b/containers/dockerfile-template
@@ -1,4 +1,4 @@
-FROM continuumio/miniconda3
+FROM containers.ligo.org/docker/base:conda
 LABEL name="bilby Base miniconda3" \
 maintainer="Gregory Ashton <gregory.ashton@ligo.org>"
 
diff --git a/containers/v2-dockerfile-test-suite-python35 b/containers/v2-dockerfile-test-suite-python35
index 38d4c8b2fcaebd5dc78e87cd699cc8419b69c1dc..544b562f37dc870538b71a4207139a90e8101c5d 100644
--- a/containers/v2-dockerfile-test-suite-python35
+++ b/containers/v2-dockerfile-test-suite-python35
@@ -1,6 +1,6 @@
 # This dockerfile is written automatically and should not be modified by hand.
 
-FROM continuumio/miniconda3
+FROM containers.ligo.org/docker/base:conda
 LABEL name="bilby Base miniconda3" \
 maintainer="Gregory Ashton <gregory.ashton@ligo.org>"
 
diff --git a/containers/v2-dockerfile-test-suite-python36 b/containers/v2-dockerfile-test-suite-python36
index ae75fff03aa80026a56fa48bc7682f8acb852881..d9a2782cfb22c020520375665ff1668e7c5f6aa4 100644
--- a/containers/v2-dockerfile-test-suite-python36
+++ b/containers/v2-dockerfile-test-suite-python36
@@ -1,6 +1,6 @@
 # This dockerfile is written automatically and should not be modified by hand.
 
-FROM continuumio/miniconda3
+FROM containers.ligo.org/docker/base:conda
 LABEL name="bilby Base miniconda3" \
 maintainer="Gregory Ashton <gregory.ashton@ligo.org>"
 
diff --git a/containers/v2-dockerfile-test-suite-python37 b/containers/v2-dockerfile-test-suite-python37
index 9e27fcab40ae8a7c0561a32b508449e0943925a6..b77f489227fd58720727a24d1a8989cc7b9dc36a 100644
--- a/containers/v2-dockerfile-test-suite-python37
+++ b/containers/v2-dockerfile-test-suite-python37
@@ -1,6 +1,6 @@
 # This dockerfile is written automatically and should not be modified by hand.
 
-FROM continuumio/miniconda3
+FROM containers.ligo.org/docker/base:conda
 LABEL name="bilby Base miniconda3" \
 maintainer="Gregory Ashton <gregory.ashton@ligo.org>"
 
diff --git a/containers/v2-dockerfile-test-suite-python38 b/containers/v2-dockerfile-test-suite-python38
index 690055494c8798269a17a80386fe76de313a81ad..71cf85d0197a80f01ef5e7765d3a2875e40d8a11 100644
--- a/containers/v2-dockerfile-test-suite-python38
+++ b/containers/v2-dockerfile-test-suite-python38
@@ -1,6 +1,6 @@
 # This dockerfile is written automatically and should not be modified by hand.
 
-FROM continuumio/miniconda3
+FROM containers.ligo.org/docker/base:conda
 LABEL name="bilby Base miniconda3" \
 maintainer="Gregory Ashton <gregory.ashton@ligo.org>"
 
diff --git a/examples/core_examples/multidimensional_gaussian.py b/examples/core_examples/multidimensional_gaussian.py
index 451afe3e06b8c6904de63b4991a34225d850b14c..c0078d50bb7e1e9b203b6b547b1822f8f46f8d87 100644
--- a/examples/core_examples/multidimensional_gaussian.py
+++ b/examples/core_examples/multidimensional_gaussian.py
@@ -33,6 +33,7 @@ class MultidimGaussianLikelihood(bilby.Likelihood):
         """
 
     def __init__(self, data, dim):
+        super().__init__()
         self.dim = dim
         self.data = np.array(data)
         self.N = len(data)
diff --git a/sampler_requirements.txt b/sampler_requirements.txt
index 06a065393d711783bbd43dbb1e3a7ccc8b8b9560..08202cc3890a9fa56ea2b84a0f786f7568d515cb 100644
--- a/sampler_requirements.txt
+++ b/sampler_requirements.txt
@@ -7,5 +7,5 @@ pymc3==3.6; python_version <= '2.7'
 pymc3>=3.6; python_version > '3.4'
 pymultinest
 kombine
-ultranest>=2.2.1
-dnest4
\ No newline at end of file
+ultranest>=3.0.0
+dnest4
diff --git a/setup.py b/setup.py
index daf2db2fc7252f02126b564695a821e93e2ee7c4..0882ade9b33e1477ff5ffccad7ee8a22be36fecc 100644
--- a/setup.py
+++ b/setup.py
@@ -64,7 +64,7 @@ def readfile(filename):
     return filecontents
 
 
-VERSION = '1.0.3'
+VERSION = '1.0.4'
 version_file = write_version_file(VERSION)
 long_description = get_long_description()
 
diff --git a/test/core/sampler/ultranest_test.py b/test/core/sampler/ultranest_test.py
index 76354e08281c379d28eda0abd9d18992220c4989..e70a70fdfb7785bd2457c01ee6399d022e88a91e 100644
--- a/test/core/sampler/ultranest_test.py
+++ b/test/core/sampler/ultranest_test.py
@@ -44,7 +44,7 @@ class TestUltranest(unittest.TestCase):
             log_interval=None,
             dlogz=None,
             max_iters=None,
-            update_interval_iter_fraction=0.2,
+            update_interval_volume_fraction=0.2,
             viz_callback=None,
             dKL=0.5,
             frac_remain=0.01,
@@ -79,7 +79,7 @@ class TestUltranest(unittest.TestCase):
             log_interval=None,
             dlogz=None,
             max_iters=None,
-            update_interval_iter_fraction=0.2,
+            update_interval_volume_fraction=0.2,
             viz_callback=None,
             dKL=0.5,
             frac_remain=0.01,
diff --git a/test/gw/detector/strain_data_test.py b/test/gw/detector/strain_data_test.py
index a11e347df563b96afdde5e025abc47974a5fc989..9cba741042a714e9875e2995d55bc628d7b78cfd 100644
--- a/test/gw/detector/strain_data_test.py
+++ b/test/gw/detector/strain_data_test.py
@@ -35,6 +35,49 @@ class TestInterferometerStrainData(unittest.TestCase):
                 np.array_equal(self.ifosd.frequency_mask, [False, True, False])
             )
 
+    def test_frequency_mask_2(self):
+        strain_data = bilby.gw.detector.InterferometerStrainData(
+            minimum_frequency=20, maximum_frequency=512)
+        strain_data.set_from_time_domain_strain(
+            time_domain_strain=np.random.normal(0, 1, 4096),
+            time_array=np.arange(0, 4, 4 / 4096)
+        )
+
+        # Test from init
+        freqs = strain_data.frequency_array[strain_data.frequency_mask]
+        self.assertTrue(all(freqs >= 20))
+        self.assertTrue(all(freqs <= 512))
+
+        # Test from update
+        strain_data.minimum_frequency = 30
+        strain_data.maximum_frequency = 256
+        freqs = strain_data.frequency_array[strain_data.frequency_mask]
+        self.assertTrue(all(freqs >= 30))
+        self.assertTrue(all(freqs <= 256))
+
+    def test_notches_frequency_mask(self):
+        strain_data = bilby.gw.detector.InterferometerStrainData(
+            minimum_frequency=20, maximum_frequency=512, notch_list=[(100, 101)])
+        strain_data.set_from_time_domain_strain(
+            time_domain_strain=np.random.normal(0, 1, 4096),
+            time_array=np.arange(0, 4, 4 / 4096)
+        )
+
+        # Test from init
+        freqs = strain_data.frequency_array[strain_data.frequency_mask]
+        idxs = (freqs > 100) * (freqs < 101)
+        self.assertTrue(len(freqs[idxs]) == 0)
+
+        # Test from setting
+        idxs = (freqs > 200) * (freqs < 201)
+        self.assertTrue(len(freqs[idxs]) > 0)
+        strain_data.notch_list = [(100, 101), (200, 201)]
+        freqs = strain_data.frequency_array[strain_data.frequency_mask]
+        idxs = (freqs > 200) * (freqs < 201)
+        self.assertTrue(len(freqs[idxs]) == 0)
+        idxs = (freqs > 100) * (freqs < 101)
+        self.assertTrue(len(freqs[idxs]) == 0)
+
     def test_set_data_fails(self):
         with mock.patch("bilby.core.utils.create_frequency_series") as m:
             m.return_value = [1, 2, 3]
@@ -316,5 +359,67 @@ class TestInterferometerStrainDataEquals(unittest.TestCase):
         self.assertNotEqual(self.ifosd_1, self.ifosd_2)
 
 
+class TestNotch(unittest.TestCase):
+    def setUp(self):
+        self.minimum_frequency = 20
+        self.maximum_frequency = 1024
+
+    def test_init(self):
+        notch = bilby.gw.detector.strain_data.Notch(self.minimum_frequency, self.maximum_frequency)
+        self.assertEqual(notch.minimum_frequency, self.minimum_frequency)
+        self.assertEqual(notch.maximum_frequency, self.maximum_frequency)
+
+    def test_init_fail(self):
+        # Infinite frequency
+        with self.assertRaises(ValueError):
+            bilby.gw.detector.strain_data.Notch(self.minimum_frequency, np.inf)
+
+        # Negative frequency
+        with self.assertRaises(ValueError):
+            bilby.gw.detector.strain_data.Notch(-10, 1024)
+        with self.assertRaises(ValueError):
+            bilby.gw.detector.strain_data.Notch(10, -1024)
+
+        # Ordering
+        with self.assertRaises(ValueError):
+            bilby.gw.detector.strain_data.Notch(30, 20)
+
+    def test_idxs(self):
+        notch = bilby.gw.detector.strain_data.Notch(self.minimum_frequency, self.maximum_frequency)
+        freqs = np.linspace(0, 2048, 100)
+        idxs = notch.get_idxs(freqs)
+        self.assertEqual(len(idxs), len(freqs))
+        freqs_masked = freqs[idxs]
+        self.assertTrue(all(freqs_masked > notch.minimum_frequency))
+        self.assertTrue(all(freqs_masked < notch.maximum_frequency))
+
+
+class TestNotchList(unittest.TestCase):
+
+    def test_init_single(self):
+        notch_list_of_tuples = [(32, 34)]
+        notch_list = bilby.gw.detector.strain_data.NotchList(notch_list_of_tuples)
+        self.assertEqual(len(notch_list), len(notch_list_of_tuples))
+        for notch, notch_tuple in zip(notch_list, notch_list_of_tuples):
+            self.assertEqual(notch.minimum_frequency, notch_tuple[0])
+            self.assertEqual(notch.maximum_frequency, notch_tuple[1])
+
+    def test_init_multiple(self):
+        notch_list_of_tuples = [(32, 34), (56, 59)]
+        notch_list = bilby.gw.detector.strain_data.NotchList(notch_list_of_tuples)
+        self.assertEqual(len(notch_list), len(notch_list_of_tuples))
+        for notch, notch_tuple in zip(notch_list, notch_list_of_tuples):
+            self.assertEqual(notch.minimum_frequency, notch_tuple[0])
+            self.assertEqual(notch.maximum_frequency, notch_tuple[1])
+
+    def test_init_fail(self):
+        with self.assertRaises(ValueError):
+            bilby.gw.detector.strain_data.NotchList([20, 30])
+        with self.assertRaises(ValueError):
+            bilby.gw.detector.strain_data.NotchList([(30, 20), (20)])
+        with self.assertRaises(ValueError):
+            bilby.gw.detector.strain_data.NotchList([(30, 20, 20)])
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/test/gw/plot_test.py b/test/gw/plot_test.py
index 2a33fdec04df34257dbc76aaf07dc22f5a72d01e..e85aac75a6582834c504e66ab66aa7a90077a484 100644
--- a/test/gw/plot_test.py
+++ b/test/gw/plot_test.py
@@ -140,7 +140,7 @@ class TestCBCResult(unittest.TestCase):
             objid="test",
             instruments="H1L1",
             load_pickle=True,
-            colorbar=True,
+            colorbar=False,
         )
 
 
diff --git a/test/gw/source_test.py b/test/gw/source_test.py
index d14517acf9549d08e7d329532d47eb4475bdb85f..ec4a2929ad96638b541ae7d39ac567d2c9fe7c4d 100644
--- a/test/gw/source_test.py
+++ b/test/gw/source_test.py
@@ -79,6 +79,19 @@ class TestLalBBH(unittest.TestCase):
     #         bilby.gw.source.lal_binary_black_hole(
     #             self.frequency_array, **self.parameters), dict)
 
+    def test_lal_bbh_xpprecession_version(self):
+        self.parameters.update(self.waveform_kwargs)
+        self.parameters["waveform_approximant"] = "IMRPhenomXP"
+
+        # Test that we can modify the XP precession version
+        out_v223 = bilby.gw.source.lal_binary_black_hole(
+            self.frequency_array, PhenomXPrecVersion=223, **self.parameters
+        )
+        out_v102 = bilby.gw.source.lal_binary_black_hole(
+            self.frequency_array, PhenomXPrecVersion=102, **self.parameters
+        )
+        self.assertFalse(np.all(out_v223["plus"] == out_v102["plus"]))
+
 
 class TestLalBNS(unittest.TestCase):
     def setUp(self):