def surface_normal(self, param):
        """Unit vector perpendicular to the detector surface at ``param``.

        The orientation is chosen as follows:

            - In 2D, the system ``(normal, tangent)`` should be
              right-handed.
            - In 3D, the system ``(tangent[0], tangent[1], normal)``
              should be right-handed.

        Here, ``tangent`` is the return value of `surface_deriv` at
        ``param``.

        Parameters
        ----------
        param : `array-like` or sequence
            Parameter value(s) at which to evaluate.  If ``ndim >= 2``,
            a sequence of length `ndim` must be provided.

        Returns
        -------
        normal : `numpy.ndarray`
            Unit vector(s) perpendicular to the detector surface at
            ``param``.
            If ``param`` is a single parameter, an array of shape
            ``(space_ndim,)`` representing a single vector is returned.
            Otherwise the shape of the returned array is

            - ``param.shape + (space_ndim,)`` if `ndim` is 1,
            - ``param.shape[:-1] + (space_ndim,)`` otherwise.
        """
        # Checking is done by `surface_deriv`
        if self.ndim == 1 and self.space_ndim == 2:
            return -perpendicular_vector(self.surface_deriv(param))
        elif self.ndim == 2 and self.space_ndim == 3:
            deriv = self.surface_deriv(param)
            if deriv.ndim > 2:
                # Vectorized, need to reshape (N, 2, 3) to (2, N, 3)
                deriv = moveaxis(deriv, -2, 0)
            normal = np.cross(*deriv, axis=-1)
            normal /= np.linalg.norm(normal, axis=-1, keepdims=True)
            return normal
        else:
            raise NotImplementedError(
                'no default implementation of `surface_normal` available '
                'for `ndim = {}` and `space_ndim = {}`'
                ''.format(self.ndim, self.space_ndim))
Unit vector perpendicular to the detector surface at ``param``.

        The orientation is chosen as follows:

            - In 2D, the system ``(normal, tangent)`` should be
              right-handed.
            - In 3D, the system ``(tangent[0], tangent[1], normal)``
              should be right-handed.

        Here, ``tangent`` is the return value of `surface_deriv` at
        ``param``.

        Parameters
        ----------
        param : `array-like` or sequence
            Parameter value(s) at which to evaluate.  If ``ndim >= 2``,
            a sequence of length `ndim` must be provided.

        Returns
        -------
        normal : `numpy.ndarray`
            Unit vector(s) perpendicular to the detector surface at
            ``param``.
            If ``param`` is a single parameter, an array of shape
            ``(space_ndim,)`` representing a single vector is returned.
            Otherwise the shape of the returned array is

            - ``param.shape + (space_ndim,)`` if `ndim` is 1,
            - ``param.shape[:-1] + (space_ndim,)`` otherwise.