From f9da271ae1ca467167065062e9923bc6d3324952 Mon Sep 17 00:00:00 2001
From: Jan Caron <j.caron@fz-juelich.de>
Date: Thu, 25 Jun 2015 19:57:15 +0200
Subject: [PATCH] New quiver plots (with color) and argument structure changes.
 costfunction: Now takes a ForwardModel as input instead of DataSet.
 forwardmodel: New private function for processing one image
 (multiprocessing). magdata: Quiver Plots (2D/3D) now support color encoding
 (angle or amplitude).          Private method for creating the appropriate
 colormap. reconstruction: Now takes Costfunction as argument instead of
 DataSet. regularisator: Introduced ComboRegularisator. scripts: New scripts.

---
 .hgignore                                     |   1 +
 pyramid/costfunction.py                       |  17 +-
 pyramid/forwardmodel.py                       |   4 +
 pyramid/magdata.py                            | 121 ++++++++++--
 pyramid/reconstruction.py                     |  92 +++++----
 pyramid/regularisator.py                      | 173 +++++++++--------
 pyramid/version.py                            |   2 +-
 .../magdata/magcreator/create_homog_slab.py   |   2 +-
 .../magdata/magcreator/create_singularity.py  |  26 +++
 scripts/magdata/magdata_from_dat.py           |  68 +++++++
 scripts/temp/custom_colormap.py               | 174 ++++++++++++++++++
 scripts/temp/quiver_plot_enhanced.py          | 127 +++++++++++++
 tests/test_compliance.py                      |   2 +-
 tests/test_costfunction.py                    |   3 +-
 tests/test_regularisator.py                   |   5 +-
 15 files changed, 655 insertions(+), 162 deletions(-)
 create mode 100644 scripts/magdata/magcreator/create_singularity.py
 create mode 100644 scripts/magdata/magdata_from_dat.py
 create mode 100644 scripts/temp/custom_colormap.py
 create mode 100644 scripts/temp/quiver_plot_enhanced.py

diff --git a/.hgignore b/.hgignore
index c604c2c..ffc7d0e 100644
--- a/.hgignore
+++ b/.hgignore
@@ -4,6 +4,7 @@ syntax: glob
 .spyderworkspace
 .spyderproject
 .cproject
+.pyramid.egg-info
 *.pyc
 *.pyd
 *.so
diff --git a/pyramid/costfunction.py b/pyramid/costfunction.py
index e534283..41fb1b5 100644
--- a/pyramid/costfunction.py
+++ b/pyramid/costfunction.py
@@ -8,7 +8,6 @@ the so called `cost` of a threedimensional magnetization distribution."""
 
 import numpy as np
 
-from pyramid.forwardmodel import ForwardModel
 from pyramid.regularisator import NoneRegularisator
 
 import logging
@@ -30,8 +29,6 @@ class Costfunction(object):
 
     Attributes
     ----------
-    data_set: :class:`~dataset.DataSet`
-        :class:`~dataset.DataSet` object, which stores all information for the cost calculation.
     regularisator : :class:`~.Regularisator`
         Regularisator class that's responsible for the regularisation term.
     y : :class:`~numpy.ndarray` (N=1)
@@ -51,14 +48,14 @@ class Costfunction(object):
 
     _log = logging.getLogger(__name__+'.Costfunction')
 
-    def __init__(self, data_set, regularisator):
+    def __init__(self, fwd_model, regularisator):
         self._log.debug('Calling __init__')
-        self.data_set = data_set
-        self.fwd_model = ForwardModel(data_set)
+        self.fwd_model = fwd_model
         self.regularisator = regularisator
         if self.regularisator is None:
             self.regularisator = NoneRegularisator()
         # Extract important information:
+        data_set = fwd_model.data_set
         self.y = data_set.phase_vec
         self.n = data_set.n
         self.m = data_set.m
@@ -69,13 +66,13 @@ class Costfunction(object):
 
     def __repr__(self):
         self._log.debug('Calling __repr__')
-        return '%s(data_set=%r, regularisator=%r)' % \
-            (self.__class__, self.data_set, self.regularisator)
+        return '%s(fwd_model=%r, regularisator=%r)' % \
+            (self.__class__, self.fwd_model, self.regularisator)
 
     def __str__(self):
         self._log.debug('Calling __str__')
-        return 'Costfunction(data_set=%s, fwd_model=%s, regularisator=%s)' % \
-            (self.data_set, self.fwd_model, self.regularisator)
+        return 'Costfunction(fwd_model=%s, fwd_model=%s, regularisator=%s)' % \
+            (self.fwd_model, self.fwd_model, self.regularisator)
 
     def __call__(self, x):
         delta_y = self.fwd_model(x) - self.y
diff --git a/pyramid/forwardmodel.py b/pyramid/forwardmodel.py
index e46f256..41a9e6e 100644
--- a/pyramid/forwardmodel.py
+++ b/pyramid/forwardmodel.py
@@ -136,6 +136,10 @@ class ForwardModel(object):
             result[hp[i]:hp[i+1]] = res
         return result
 
+    def _jac_dot_element(self, mag_vec, projector, phasemapper):
+            return phasemapper.jac_dot(projector.jac_dot(mag_vec))
+
+
     def jac_T_dot(self, x, vector):
         ''''Calculate the product of the transposed Jacobi matrix with a given `vector`.
 
diff --git a/pyramid/magdata.py b/pyramid/magdata.py
index 0084ed3..af24642 100644
--- a/pyramid/magdata.py
+++ b/pyramid/magdata.py
@@ -5,12 +5,15 @@
 """This module provides the :class:`~.MagData` class for storing of magnetization data."""
 
 
+from __future__ import division
+
 import os
 
 import numpy as np
 from numpy.linalg import norm
 from scipy.ndimage.interpolation import zoom
 
+import matplotlib as mpl
 import matplotlib.pyplot as plt
 import matplotlib.cm as cmx
 from matplotlib.ticker import MaxNLocator
@@ -164,6 +167,45 @@ class MagData(object):
         self._log.debug('Calling __imul__')
         return self.__mul__(other)
 
+    @classmethod
+    def _create_directional_colormap(cls, levels=15, N=256):
+        cls._log.debug('Calling __create_directional_colormap')
+        r, g, b = [], [], []  # RGB lists
+        # Create saturation lists to encode up and down directions via black and white colors.
+        # example for 5 levels from black (down) to color (in-plane) to white:
+        # pos_sat: [ 0.   0.5  1.   1.   1. ]
+        # neg_sat: [ 0.   0.   0.   0.5  1. ]
+        center = levels//2
+        pos_sat = np.ones(levels)
+        pos_sat[0:center] = [i/center for i in range(center)]
+        neg_sat = np.zeros(levels)
+        neg_sat[center+1:] = [(i+1)/center for i in range(center)]
+
+        # Iterate over all levels (the center level represents in-plane moments!):
+        for i in range(levels):
+            inter_points = np.linspace(i/levels, (i+1)/levels, 5)  # interval points, current level
+            # Red:
+            r.append((inter_points[0], 0, neg_sat[i]))
+            r.append((inter_points[1], pos_sat[i], pos_sat[i]))
+            r.append((inter_points[2], pos_sat[i], pos_sat[i]))
+            r.append((inter_points[3], neg_sat[i], neg_sat[i]))
+            r.append((inter_points[4], neg_sat[i], 0))
+            # Green:
+            g.append((inter_points[0], 0, neg_sat[i]))
+            g.append((inter_points[1], neg_sat[i], neg_sat[i]))
+            g.append((inter_points[2], pos_sat[i], pos_sat[i]))
+            g.append((inter_points[3], pos_sat[i], pos_sat[i]))
+            g.append((inter_points[4], neg_sat[i], 0))
+            # Blue
+            b.append((inter_points[0], 0, pos_sat[i]))
+            b.append((inter_points[1], neg_sat[i], neg_sat[i]))
+            b.append((inter_points[2], neg_sat[i], neg_sat[i]))
+            b.append((inter_points[3], neg_sat[i], neg_sat[i]))
+            b.append((inter_points[4], pos_sat[i], 0))
+        # Combine to color dictionary and return:
+        cdict = {'red': r, 'green': g, 'blue': b}
+        return mpl.colors.LinearSegmentedColormap('directional_colormap', cdict, N)
+
     def copy(self):
         '''Returns a copy of the :class:`~.MagData` object
 
@@ -625,7 +667,7 @@ class MagData(object):
         tree.write(filename, pretty_print=True)
 
     def quiver_plot(self, title='Magnetization Distribution', axis=None, proj_axis='z',
-                    color='b', ar_dens=1, ax_slice=None, log=False, scaled=True, scale=1.):
+                    coloring='angle', ar_dens=1, ax_slice=None, log=False, scaled=True, scale=1.):
         '''Plot a slice of the magnetization as a quiver plot.
 
         Parameters
@@ -636,8 +678,8 @@ class MagData(object):
             Axis on which the graph is plotted. Creates a new figure if none is specified.
         proj_axis : {'z', 'y', 'x'}, optional
             The axis, from which a slice is plotted. The default is 'z'.
-        color : string
-            Color of the arrows. Default is black ('b').
+        coloring : string
+            Color coding mode of the arrows. Use 'angle' (default), 'amplitude' or 'uniform'.
         ar_dens: int (optional)
             Number defining the arrow density which is plotted. A higher ar_dens number skips more
             arrows (a number of 2 plots every second arrow). Default is 1.
@@ -688,33 +730,53 @@ class MagData(object):
             plot_v = np.swapaxes(np.copy(self.magnitude[1][..., ax_slice]), 0, 1)  # y-component
             u_label = 'z [px]'
             v_label = 'y [px]'
+        # Prepare quiver (select only used arrows if ar_dens is specified):
+        dim_uv = plot_u.shape
+        yy, xx = np.indices(dim_uv)
+        xx = xx[::ar_dens, ::ar_dens]
+        yy = yy[::ar_dens, ::ar_dens]
+        plot_u = plot_u[::ar_dens, ::ar_dens]
+        plot_v = plot_v[::ar_dens, ::ar_dens]
+        amplitudes = np.hypot(plot_u, plot_v)
+        angles = np.angle(plot_u+1j*plot_v, deg=True)
+        # Calculate the arrow colors:
+        if coloring == 'angle':
+            self._log.debug('Encoding angles')
+            colors = (1 - np.arctan2(plot_v, plot_u)/np.pi) / 2  # in-plane angle (rescaled: 0 - 1)
+            cmap = self._create_directional_colormap(levels=1, N=256)
+        elif coloring == 'amplitude':
+            self._log.debug('Encoding amplitude')
+            colors = amplitudes / amplitudes.max()
+            cmap = 'jet'
+        elif coloring == 'uniform':
+            self._log.debug('No color encoding')
+            colors = np.zeros_like(plot_u)  # use black arrows!
+            cmap = 'gray'
+        else:
+            raise AttributeError("Invalid coloring mode! Use 'angles', 'amplitude' or 'uniform'!")
         # If no axis is specified, a new figure is created:
         if axis is None:
             self._log.debug('axis is None')
             fig = plt.figure(figsize=(8.5, 7))
             axis = fig.add_subplot(1, 1, 1)
         axis.set_aspect('equal')
-        angles = np.angle(plot_u+1j*plot_v, deg=True)
         # Take the logarithm of the arrows to clearly show directions (if specified):
         if log:
             cutoff = 10
-            amp = np.round(np.hypot(plot_u, plot_v), decimals=cutoff)
+            amp = np.round(amplitudes, decimals=cutoff)
             min_value = amp[np.nonzero(amp)].min()
             plot_u = np.round(plot_u, decimals=cutoff) / min_value
             plot_u = np.log10(np.abs(plot_u)+1) * np.sign(plot_u)
             plot_v = np.round(plot_v, decimals=cutoff) / min_value
             plot_v = np.log10(np.abs(plot_v)+1) * np.sign(plot_v)
+            amplitudes = np.hypot(plot_u, plot_v)  # Recalculate!
         # Scale the magnitude of the arrows to the highest one (if specified):
         if scaled:
-            plot_u /= np.hypot(plot_u, plot_v).max()
-            plot_v /= np.hypot(plot_u, plot_v).max()
-        # Setup quiver:
-        dim_uv = plot_u.shape
-        ad = ar_dens
-        yy, xx = np.indices(dim_uv)
-        axis.quiver(xx[::ad, ::ad], yy[::ad, ::ad], plot_u[::ad, ::ad], plot_v[::ad, ::ad],
-                    pivot='middle', units='xy', angles=angles[::ad, ::ad], minlength=0.25,
-                    scale_units='xy', scale=scale/ad, headwidth=6, headlength=7, color=color)
+            plot_u /= amplitudes.max()
+            plot_v /= amplitudes.max()
+        axis.quiver(xx, yy, plot_u, plot_v, colors, cmap=cmap, angles=angles,
+                    pivot='middle', units='xy', scale_units='xy', scale=scale/ar_dens,
+                    minlength=0.25, headwidth=6, headlength=7)
         axis.set_xlim(-1, dim_uv[1])
         axis.set_ylim(-1, dim_uv[0])
         axis.set_title(title, fontsize=18)
@@ -726,7 +788,7 @@ class MagData(object):
         return axis
 
     def quiver_plot3d(self, title='Magnetization Distribution', limit=None, cmap='jet',
-                      ar_dens=1, mode='arrow', show_pipeline=False):
+                      ar_dens=1, mode='arrow', coloring='angle', show_pipeline=False):
         '''Plot the magnetization as 3D-vectors in a quiverplot.
 
         Parameters
@@ -743,6 +805,8 @@ class MagData(object):
         mode: string, optional
             Mode, determining the glyphs used in the 3D plot. Default is 'arrow', which corresponds
             to 3D arrows. For large amounts of arrows, '2darrow' should be used.
+        coloring : string
+            Color coding mode of the arrows. Use 'angle' (default) or 'amplitude'.
         show_pipeline : boolean, optional
             If True, the mayavi pipeline, a GUI used for image manipulation is shown. The default
             is False.
@@ -771,13 +835,36 @@ class MagData(object):
         # Plot them as vectors:
         mlab.figure(size=(750, 700))
         plot = mlab.quiver3d(xx, yy, zz, x_mag, y_mag, z_mag, mode=mode, colormap=cmap)
+        if coloring == 'angle':  # Encodes the full angle via colorwheel and saturation
+            self._log.debug('Encoding full 3D angles')
+            from tvtk.api import tvtk
+            phis = (1 - np.arctan2(y_mag, x_mag)/np.pi) / 2  # in-plane angle (rescaled: 0 to 1)
+            thetas = np.arctan2(np.hypot(y_mag, x_mag), z_mag)/np.pi  # vert. angle (also rescaled)
+            levels = 15  # number of shades for up/down arrows (white is up, black is down)
+            N = 256  # Overall number of colors for the colormap (only N/levels per level!)
+            cmap = self._create_directional_colormap(levels, N)
+            colors = []
+            for i in range(len(x_mag)):
+                level = np.floor((1-thetas[i]) * levels)
+                lookup_value = (level + phis[i]) / levels
+                rgba = tuple((np.asarray(cmap(lookup_value)) * (N-1)).astype(np.int))
+                colors.append(rgba)
+            sc = tvtk.UnsignedCharArray()  # Used to hold the colors
+            sc.from_array(colors)
+            plot.mlab_source.dataset.point_data.scalars = sc
+            plot.mlab_source.dataset.modified()
+            plot.glyph.color_mode = 'color_by_scalar'
+        elif coloring == 'amplitude':  # Encodes the amplitude of the arrows with the jet colormap
+            self._log.debug('Encoding amplitude')
+            mlab.colorbar(label_fmt='%.2f')
+            mlab.colorbar(orientation='vertical')
+        else:
+            raise AttributeError('Coloring mode not supported!')
         plot.glyph.glyph_source.glyph_position = 'center'
         plot.module_manager.vector_lut_manager.data_range = np.array([0, limit])
         mlab.outline(plot)
         mlab.axes(plot)
         mlab.title(title, height=0.95, size=0.35)
-        mlab.colorbar(label_fmt='%.2f')
-        mlab.colorbar(orientation='vertical')
         mlab.orientation_axes()
         if show_pipeline:
             mlab.show_pipeline()
diff --git a/pyramid/reconstruction.py b/pyramid/reconstruction.py
index f5df52a..96664e8 100644
--- a/pyramid/reconstruction.py
+++ b/pyramid/reconstruction.py
@@ -15,7 +15,6 @@ the distribution.
 
 import numpy as np
 
-from pyramid.costfunction import Costfunction
 from pyramid.magdata import MagData
 
 import logging
@@ -25,20 +24,18 @@ __all__ = ['optimize_linear', 'optimize_nonlin', 'optimize_splitbregman']
 _log = logging.getLogger(__name__)
 
 
-def optimize_linear(data, regularisator=None, max_iter=None):
+def optimize_linear(costfunction, max_iter=None):
     '''Reconstruct a three-dimensional magnetic distribution from given phase maps via the
     conjugate gradient optimizaion method :func:`~.scipy.sparse.linalg.cg`.
     Blazingly fast for l2-based cost functions.
 
     Parameters
     ----------
-    data : :class:`~.DataSet`
-        :class:`~.DataSet` object containing all phase maps in :class:`~.PhaseMap` objects and all
-        projection directions in :class:`~.Projector` objects. These provide the essential
-        information for the reconstruction.
-    regularisator : :class:`~.Regularisator`, optional
-        Regularisator class that's responsible for the regularisation term. Defaults to zero
-        order Tikhonov if none is provided.
+    costfunction : :class:`~.Costfunction`
+        A :class:`~.Costfunction` object which implements a specified forward model and
+        regularisator which is minimized in the optimization process.
+    max_iter : int, optional
+        The maximum number of iterations for the opimization.
 
     Returns
     -------
@@ -48,32 +45,28 @@ def optimize_linear(data, regularisator=None, max_iter=None):
     '''
     import jutil.cg as jcg
     _log.debug('Calling optimize_linear')
-    # Set up necessary objects:
-    cost = Costfunction(data, regularisator)
-    _log.info('Cost before optimization: {}'.format(cost(np.zeros(cost.n))))
-    x_opt = jcg.conj_grad_minimize(cost, max_iter=max_iter).x
-    _log.info('Cost after optimization: {}'.format(cost(x_opt)))
+    _log.info('Cost before optimization: {}'.format(costfunction(np.zeros(costfunction.n))))
+    x_opt = jcg.conj_grad_minimize(costfunction, max_iter=max_iter).x
+    _log.info('Cost after optimization: {}'.format(costfunction(x_opt)))
     # Create and return fitting MagData object:
-    mag_opt = MagData(data.a, np.zeros((3,) + data.dim))
-    mag_opt.set_vector(x_opt, data.mask)
-    return mag_opt, cost
+    data_set = costfunction.fwd_model.data_set
+    mag_opt = MagData(data_set.a, np.zeros((3,) + data_set.dim))
+    mag_opt.set_vector(x_opt, data_set.mask)
+    return mag_opt
 
 
-def optimize_nonlin(data, first_guess=None, regularisator=None):
+def optimize_nonlin(costfunction, first_guess=None):
     '''Reconstruct a three-dimensional magnetic distribution from given phase maps via
     steepest descent method. This is slow, but works best for non l2-regularisators.
 
 
     Parameters
     ----------
-    data : :class:`~.DataSet`
-        :class:`~.DataSet` object containing all phase maps in :class:`~.PhaseMap` objects and all
-        projection directions in :class:`~.Projector` objects. These provide the essential
-        information for the reconstruction.
+    costfunction : :class:`~.Costfunction`
+        A :class:`~.Costfunction` object which implements a specified forward model and
+        regularisator which is minimized in the optimization process.
     first_guess : :class:`~pyramid.magdata.MagData`
         magnetization to start the non-linear iteration with.
-    regularisator : :class:`~.Regularisator`, optional
-        Regularisator class that's responsible for the regularisation term.
 
     Returns
     -------
@@ -84,14 +77,14 @@ def optimize_nonlin(data, first_guess=None, regularisator=None):
     import jutil.minimizer as jmin
     import jutil.norms as jnorms
     _log.debug('Calling optimize_nonlin')
+    data_set = costfunction.fwd_model.data_set
     if first_guess is None:
-        first_guess = MagData(data.a, np.zeros((3,) + data.dim))
+        first_guess = MagData(data_set.a, np.zeros((3,) + data_set.dim))
 
-    x_0 = first_guess.get_vector(data.mask)
-    cost = Costfunction(data, regularisator)
-    assert len(x_0) == cost.n, (len(x_0), cost.m, cost.n)
+    x_0 = first_guess.get_vector(data_set.mask)
+    assert len(x_0) == costfunction.n, (len(x_0), costfunction.m, costfunction.n)
 
-    p = regularisator.p
+    p = costfunction.regularisator.p
     q = 1. / (1. - (1. / p))
     lq = jnorms.LPPow(q, 1e-20)
 
@@ -101,20 +94,20 @@ def optimize_nonlin(data, first_guess=None, regularisator=None):
         return direc_p
 
     # This Method is semi-best for Lp type problems. Takes forever, though
-    _log.info('Cost before optimization: {}'.format(cost(np.zeros(cost.n))))
+    _log.info('Cost before optimization: {}'.format(costfunction(np.zeros(costfunction.n))))
     result = jmin.minimize(
-        cost, x_0,
+        costfunction, x_0,
         method="SteepestDescent",
         options={"preconditioner": preconditioner},
         tol={"max_iteration": 10000})
     x_opt = result.x
-    _log.info('Cost after optimization: {}'.format(cost(x_opt)))
-    mag_opt = MagData(data.a, np.zeros((3,) + data.dim))
-    mag_opt.set_vector(x_opt, data.mask)
+    _log.info('Cost after optimization: {}'.format(costfunction(x_opt)))
+    mag_opt = MagData(data_set.a, np.zeros((3,) + data_set.dim))
+    mag_opt.set_vector(x_opt, data_set.mask)
     return mag_opt
 
 
-def optimize_splitbregman(data, weight, lam, mu):
+def optimize_splitbregman(costfunction, weight, lam, mu):
     '''
     Reconstructs magnet distribution from phase image measurements using a split bregman
     algorithm with a dedicated TV-l1 norm. Very dedicated, frickle, brittle, and difficult
@@ -124,10 +117,9 @@ def optimize_splitbregman(data, weight, lam, mu):
 
     Parameters
     ----------
-    data : :class:`~.DataSet`
-        :class:`~.DataSet` object containing all phase maps in :class:`~.PhaseMap` objects and all
-        projection directions in :class:`~.Projector` objects. These provide the essential
-        information for the reconstruction.
+    costfunction : :class:`~.Costfunction`
+        A :class:`~.Costfunction` object which implements a specified forward model and
+        regularisator which is minimized in the optimization process.
     weight : float
         Obscure split bregman parameter
     lam : float
@@ -144,29 +136,27 @@ def optimize_splitbregman(data, weight, lam, mu):
     import jutil.splitbregman as jsb
     import jutil.operator as joperator
     import jutil.diff as jdiff
-    from pyramid.regularisator import FirstOrderRegularisator
     _log.debug('Calling optimize_splitbregman')
 
     # regularisator is actually not necessary, but this makes the cost
     # function to that which is supposedly optimized by split bregman.
     # Thus cost can be used to verify convergence
-    regularisator = FirstOrderRegularisator(data.mask, lam / mu, 1)
-    cost = Costfunction(data, regularisator)
-    fwd_mod = cost.fwd_model
+    fwd_model = costfunction.fwd_model
+    data_set = fwd_model.data_set
 
     A = joperator.Function(
-        (cost.m, cost.n),
-        lambda x: fwd_mod.jac_dot(None, x),
-        FT=lambda x: fwd_mod.jac_T_dot(None, x))
+        (costfunction.m, costfunction.n),
+        lambda x: fwd_model.jac_dot(None, x),
+        FT=lambda x: fwd_model.jac_T_dot(None, x))
     D = joperator.VStack([
-        jdiff.get_diff_operator(data.mask, 0, 3),
-        jdiff.get_diff_operator(data.mask, 1, 3)])
-    y = np.asarray(cost.y, dtype=np.double)
+        jdiff.get_diff_operator(data_set.mask, 0, 3),
+        jdiff.get_diff_operator(data_set.mask, 1, 3)])
+    y = np.asarray(costfunction.y, dtype=np.double)
 
     x_opt = jsb.split_bregman_2d(
         A, D, y,
         weight=weight, mu=mu, lambd=lam, max_iter=1000)
 
-    mag_opt = MagData(data.a, np.zeros((3,) + data.dim))
-    mag_opt.set_vector(x_opt, data.mask)
+    mag_opt = MagData(data_set.a, np.zeros((3,) + data_set.dim))
+    mag_opt.set_vector(x_opt, data_set.mask)
     return mag_opt
diff --git a/pyramid/regularisator.py b/pyramid/regularisator.py
index db23e8c..a6fac95 100644
--- a/pyramid/regularisator.py
+++ b/pyramid/regularisator.py
@@ -9,6 +9,7 @@ which adds additional constraints to a costfunction to minimize."""
 import abc
 
 import numpy as np
+from scipy import sparse
 
 import jutil.norms as jnorm
 import jutil.diff as jdiff
@@ -17,7 +18,8 @@ import jutil.operator as joperator
 import logging
 
 
-__all__ = ['NoneRegularisator', 'ZeroOrderRegularisator', 'FirstOrderRegularisator']
+__all__ = ['NoneRegularisator', 'ZeroOrderRegularisator', 'FirstOrderRegularisator',
+           'ComboRegularisator']
 
 
 class Regularisator(object):
@@ -117,6 +119,97 @@ class Regularisator(object):
         return self.lam * self.norm.hess_diag(x)
 
 
+class ComboRegularisator(Regularisator):
+
+    '''Class for providing a regularisation term which combines several regularisators.
+
+    If more than one regularisation should be utilized, this class can be use. It is given a list
+    of :class:`~.Regularisator` objects. The input will be forwarded to each of them and the
+    results are summed up and returned.
+
+    Attributes
+    ----------
+    reg_list: :class:`~.Regularisator`
+        A list of regularisator objects to whom the input is passed on.
+
+    '''
+
+    def __init__(self, reg_list):
+        self._log.debug('Calling __init__')
+        self.reg_list = reg_list
+        self._log.debug('Created '+str(self))
+
+    def __call__(self, x):
+        self._log.debug('Calling __call__')
+        return np.sum([self.reg_list[i](x) for i in range(len(self.reg_list))], axis=0)
+
+    def __repr__(self):
+        self._log.debug('Calling __repr__')
+        return '%s(reg_list=%r)' % (self.__class__, self.reg_list)
+
+    def __str__(self):
+        self._log.debug('Calling __str__')
+        return 'ComboRegularisator(reg_list=%s)' % (self.reg_list)
+
+    def jac(self, x):
+        '''Calculate the derivative of the regularisation term for a given magnetic distribution.
+
+        Parameters
+        ----------
+        x: :class:`~numpy.ndarray` (N=1)
+            Vectorized magnetization distribution, for which the Jacobi vector is calculated.
+
+        Returns
+        -------
+        result : :class:`~numpy.ndarray` (N=1)
+            Jacobi vector which represents the cost derivative of all voxels of the magnetization.
+
+        '''
+        return np.sum([self.reg_list[i].jac(x) for i in range(len(self.reg_list))], axis=0)
+
+    def hess_dot(self, x, vector):
+        '''Calculate the product of a `vector` with the Hessian matrix of the regularisation term.
+
+        Parameters
+        ----------
+        x : :class:`~numpy.ndarray` (N=1)
+            Vectorized magnetization distribution at which the Hessian is calculated. The Hessian
+            is constant in this case, thus `x` can be set to None (it is not used int the
+            computation). It is implemented for the case that in the future nonlinear problems
+            have to be solved.
+        vector : :class:`~numpy.ndarray` (N=1)
+            Vectorized magnetization distribution which is multiplied by the Hessian.
+
+        Returns
+        -------
+        result : :class:`~numpy.ndarray` (N=1)
+            Product of the input `vector` with the Hessian matrix.
+
+        '''
+        return np.sum([self.reg_list[i].hess_dot(x, vector) for i in range(len(self.reg_list))],
+                      axis=0)
+
+    def hess_diag(self, x):
+        ''' Return the diagonal of the Hessian.
+
+        Parameters
+        ----------
+        x : :class:`~numpy.ndarray` (N=1)
+            Vectorized magnetization distribution at which the Hessian is calculated. The Hessian
+            is constant in this case, thus `x` can be set to None (it is not used in the
+            computation). It is implemented for the case that in the future nonlinear problems
+            have to be solved.
+
+        Returns
+        -------
+        result : :class:`~numpy.ndarray` (N=1)
+            Diagonal of the Hessian matrix.
+
+        '''
+        self._log.debug('Calling hess_diag')
+        return np.sum([self.reg_list[i].hess_diag(x) for i in range(len(self.reg_list))], axis=0)
+
+
 class NoneRegularisator(Regularisator):
     '''Placeholder class if no regularization is used.
 
@@ -255,86 +348,10 @@ class FirstOrderRegularisator(Regularisator):
         D0 = jdiff.get_diff_operator(mask, 0, 3)
         D1 = jdiff.get_diff_operator(mask, 1, 3)
         D2 = jdiff.get_diff_operator(mask, 2, 3)
-        D = joperator.VStack([D0, D1, D2])
+        D = sparse.vstack([D0, D1, D2])
         if p == 2:
             norm = jnorm.WeightedL2Square(D)
         else:
             norm = jnorm.WeightedTV(jnorm.LPPow(p, 1e-12), D, [D0.shape[0], D.shape[0]])
         super(FirstOrderRegularisator, self).__init__(norm, lam)
         self._log.debug('Created '+str(self))
-
-# TODO: ComboRegularisator ########################################################################
-#class ComboRegularisator(Regularisator):
-#
-#    '''Class for providing a regularisation term which implements derivation minimization.
-#
-#    The constraint this class represents is the minimization of the first order derivative of the
-#    3D magnetization distribution using a Lp norm. Important is the regularisation parameter `lam`
-#    (lambda) which determines the weighting between the two cost parts (measurements and
-#    regularisation).
-#
-#    Attributes
-#    ----------
-#    mask: :class:`~numpy.ndarray` (N=3)
-#        A boolean mask which defines the magnetized volume in 3D.
-#    lam: float
-#        Regularisation parameter determining the weighting between measurements and regularisation.
-#    p: int, optional
-#        Order of the norm (default: 2, which means a standard L2-norm).
-#
-#    '''
-#
-#    def __init__(self, norm, lam):
-#        self._log.debug('Calling __init__')
-#        self.norm = norm
-#        self.lam = lam
-#        self._log.debug('Created '+str(self))
-#
-#    def __call__(self, x):
-#        self._log.debug('Calling __call__')
-#        return self.lam * self.norm(x)
-#
-#    def __repr__(self):
-#        self._log.debug('Calling __repr__')
-#        return '%s(norm=%r, lam=%r)' % (self.__class__, self.norm, self.lam)
-#
-#    def __str__(self):
-#        self._log.debug('Calling __str__')
-#        return 'Regularisator(norm=%s, lam=%s)' % (self.norm, self.lam)
-#
-#    def jac(self, x):
-#        '''Calculate the derivative of the regularisation term for a given magnetic distribution.
-#
-#        Parameters
-#        ----------
-#        x: :class:`~numpy.ndarray` (N=1)
-#            Vectorized magnetization distribution, for which the Jacobi vector is calculated.
-#
-#        Returns
-#        -------
-#        result : :class:`~numpy.ndarray` (N=1)
-#            Jacobi vector which represents the cost derivative of all voxels of the magnetization.
-#
-#        '''
-#        return self.lam * self.norm.jac(x)
-#
-#    def hess_dot(self, x, vector):
-#        '''Calculate the product of a `vector` with the Hessian matrix of the regularisation term.
-#
-#        Parameters
-#        ----------
-#        x : :class:`~numpy.ndarray` (N=1)
-#            Vectorized magnetization distribution at which the Hessian is calculated. The Hessian
-#            is constant in this case, thus `x` can be set to None (it is not used int the
-#            computation). It is implemented for the case that in the future nonlinear problems
-#            have to be solved.
-#        vector : :class:`~numpy.ndarray` (N=1)
-#            Vectorized magnetization distribution which is multiplied by the Hessian.
-#
-#        Returns
-#        -------
-#        result : :class:`~numpy.ndarray` (N=1)
-#            Product of the input `vector` with the Hessian matrix.
-#
-#        '''
-#        return self.lam * self.norm.hess_dot(x, vector)
diff --git a/pyramid/version.py b/pyramid/version.py
index 577dd56..bf68b51 100644
--- a/pyramid/version.py
+++ b/pyramid/version.py
@@ -1,3 +1,3 @@
 # THIS FILE IS GENERATED BY THE PYRAMID SETUP.PY
 version = "0.1.0-dev"
-hg_revision = "f641a9bb194f+"
+hg_revision = "e2e912d70bc2+"
diff --git a/scripts/magdata/magcreator/create_homog_slab.py b/scripts/magdata/magcreator/create_homog_slab.py
index 5207120..9fc206d 100644
--- a/scripts/magdata/magcreator/create_homog_slab.py
+++ b/scripts/magdata/magcreator/create_homog_slab.py
@@ -1,6 +1,6 @@
 #! python
 # -*- coding: utf-8 -*-
-"""Create pyramid logo."""
+"""Create homogeneous slab."""
 
 
 import numpy as np
diff --git a/scripts/magdata/magcreator/create_singularity.py b/scripts/magdata/magcreator/create_singularity.py
new file mode 100644
index 0000000..d050b0e
--- /dev/null
+++ b/scripts/magdata/magcreator/create_singularity.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+"""magnetic singularity"""
+
+
+import numpy as np
+import pyramid as py
+import logging.config
+
+
+logging.config.fileConfig(py.LOGGING_CONFIG, disable_existing_loggers=False)
+
+# Parameters:
+dim = (5, 5, 5)
+center = (2., 2., 2.)
+a = 1.0  # in nm
+filename = 'magdata_mc_singularity.nc'
+
+magnitude = np.zeros((3,) + dim)
+zz, yy, xx = np.indices(dim)
+magnitude = np.array((xx-center[2], yy-center[1], zz-center[0]))
+magnitude /= np.sqrt((magnitude**2).sum(axis=0))
+
+# Create and save MagData object:
+mag_data = py.MagData(a, magnitude)
+mag_data.quiver_plot3d(coloring='full angle')
+mag_data.save_to_netcdf4(filename)
diff --git a/scripts/magdata/magdata_from_dat.py b/scripts/magdata/magdata_from_dat.py
new file mode 100644
index 0000000..4a1f176
--- /dev/null
+++ b/scripts/magdata/magdata_from_dat.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+"""Create magnetization distributions from fortran sorted txt-files."""
+
+
+import os
+import numpy as np
+import pyramid as py
+import logging.config
+
+
+logging.config.fileConfig(py.LOGGING_CONFIG, disable_existing_loggers=False)
+
+###################################################################################################
+filename = 'J=1.D=0.084.H=0.0067.Bobber.dat'#'J=1.D=0.084.H=0.00353.SkLatt.dat'
+scale = 1
+###################################################################################################
+
+#@profile
+def func():
+    path = os.path.join(py.DIR_FILES, 'dat', filename)
+#    data = np.genfromtxt(path, dtype=np.float32, delimiter=',', usecols=(0, 1, 2, 3, 4, 5))
+    x, y, z, xmag, ymag, zmag = data.T
+    a = (y[1] - y[0]) * scale
+    dim_z = len(np.unique(z))
+    dim_y = len(np.unique(y))
+    dim_x = len(np.unique(x))
+    dim = (dim_z, dim_x, dim_y)  # Order of descending variance!
+    xmag = xmag.reshape(dim).swapaxes(1, 2)
+    ymag = ymag.reshape(dim).swapaxes(1, 2)
+    zmag = zmag.reshape(dim).swapaxes(1, 2)
+    magnitude = np.array((xmag, ymag, zmag))
+    mag_data = py.MagData(a, magnitude)
+    mag_data.save_to_netcdf4('magdata_dat_{}'.format(filename.replace('.dat', '.nc')))
+#    mag_data.quiver_plot3d(ar_dens=4, coloring='amplitude')
+#    mag_data.quiver_plot3d(ar_dens=4, coloring='angle')
+#    py.pm(mag_data).display_combined(interpolation='bilinear')
+
+
+@profile
+def funci():
+    path = os.path.join(py.DIR_FILES, 'dat', filename)
+    with open(path) as f:
+        import csv
+        reader = csv.reader(f)
+        x, y, z, xmag, ymag, zmag = [], [], [], [], [], []
+        for row in reader:
+            x.append(int(row[0]))
+            y.append(int(row[1]))
+            z.append(int(row[2]))
+            xmag.append(float(row[3]))
+            ymag.append(float(row[4]))
+            zmag.append(float(row[5]))
+    a = (y[1] - y[0]) * scale
+    dim_z = len(np.unique(z))
+    dim_y = len(np.unique(y))
+    dim_x = len(np.unique(x))
+    dim = (dim_z, dim_x, dim_y)  # Order of descending variance!
+    xmag = np.reshape(xmag, dim).swapaxes(1, 2)
+    ymag = np.reshape(ymag, dim).swapaxes(1, 2)
+    zmag = np.reshape(zmag, dim).swapaxes(1, 2)
+    magnitude = np.array((xmag, ymag, zmag))
+    mag_data = py.MagData(a, magnitude)
+#   mag_data.save_to_netcdf4('magdata_dat_{}'.format(filename.replace('.dat', '.nc')))
+
+if __name__ == '__main__':
+    funci()
+
+
diff --git a/scripts/temp/custom_colormap.py b/scripts/temp/custom_colormap.py
new file mode 100644
index 0000000..2ed480a
--- /dev/null
+++ b/scripts/temp/custom_colormap.py
@@ -0,0 +1,174 @@
+# -*- coding: utf-8 -*-
+"""
+Created on Sun Jun 21 12:14:28 2015
+
+@author: Jan
+"""
+
+from __future__ import division
+import numpy as np
+import matplotlib as mpl
+import matplotlib.pyplot as plt
+from mpl_toolkits.mplot3d import Axes3D
+
+import logging
+
+
+__all__ = ['create_custom_colormap']
+_log = logging.getLogger(__name__)
+
+
+def create_custom_colormap(levels=15, N=256):
+
+    '''Class for storing phase map data.
+
+    Represents 2-dimensional phase maps. The phase information itself is stored as a 2-dimensional
+    matrix in `phase`, but can also be accessed as a vector via `phase_vec`. :class:`~.PhaseMap`
+    objects support negation, arithmetic operators (``+``, ``-``, ``*``) and their augmented
+    counterparts (``+=``, ``-=``, ``*=``), with numbers and other :class:`~.PhaseMap`
+    objects, if their dimensions and grid spacings match. It is possible to load data from NetCDF4
+    or textfiles or to save the data in these formats. Methods for plotting the phase or a
+    corresponding holographic contour map are provided. Holographic contour maps are created by
+    taking the cosine of the (optionally amplified) phase and encoding the direction of the
+    2-dimensional gradient via color. The directional encoding can be seen by using the
+    :func:`~.make_color_wheel` function. Use the :func:`~.display_combined` function to plot the
+    phase map and the holographic contour map next to each other.
+
+    Attributes
+    ----------
+    a: float
+        The grid spacing in nm.
+    dim_uv: tuple (N=2)
+        Dimensions of the grid.
+    phase: :class:`~numpy.ndarray` (N=2)
+        Matrix containing the phase shift.
+    phase_vec: :class:`~numpy.ndarray` (N=2)
+        Vector containing the phase shift.
+    mask: :class:`~numpy.ndarray` (boolean, N=2, optional)
+        Mask which determines the projected magnetization distribution, gotten from MIP images or
+        otherwise acquired. Defaults to an array of ones (all pixels are considered).
+    confidence: :class:`~numpy.ndarray` (N=2, optional)
+        Confidence array which determines the trust of specific regions of the phase_map. A value
+        of 1 means the pixel is trustworthy, a value of 0 means it is not. Defaults to an array of
+        ones (full trust for all pixels). Can be used for the construction of Se_inv.
+    unit: {'rad', 'mrad'}, optional
+        Set the unit of the phase map. This is important for the :func:`display` function,
+        because the phase is scaled accordingly. Does not change the phase itself, which is
+        always in `rad`.
+
+    '''  # TODO: Docstring!
+
+    CDICT = {'red': [(0.00, 1.0, 0.0),
+                     (0.25, 1.0, 1.0),
+                     (0.50, 1.0, 1.0),
+                     (0.75, 0.0, 0.0),
+                     (1.00, 0.0, 1.0)],
+
+             'green': [(0.00, 0.0, 0.0),
+                       (0.25, 0.0, 0.0),
+                       (0.50, 1.0, 1.0),
+                       (0.75, 1.0, 1.0),
+                       (1.00, 0.0, 1.0)],
+
+             'blue': [(0.00, 1.0, 1.0),
+                      (0.25, 0.0, 0.0),
+                      (0.50, 0.0, 0.0),
+                      (0.75, 0.0, 0.0),
+                      (1.00, 1.0, 1.0)]}
+
+    CDICT_INV = {'red': [(0.00, 0.0, 1.0),
+                         (0.25, 0.0, 0.0),
+                         (0.50, 0.0, 0.0),
+                         (0.75, 1.0, 1.0),
+                         (1.00, 1.0, 0.0)],
+
+                 'green': [(0.00, 1.0, 1.0),
+                           (0.25, 1.0, 1.0),
+                           (0.50, 0.0, 0.0),
+                           (0.75, 0.0, 0.0),
+                           (1.00, 1.0, 0.0)],
+
+                 'blue': [(0.00, 0.0, 0.0),
+                          (0.25, 1.0, 1.0),
+                          (0.50, 1.0, 1.0),
+                          (0.75, 1.0, 1.0),
+                          (1.00, 0.0, 0.0)]}
+
+    r, g, b = [], [], []
+    center = levels//2
+    pos_sat = np.ones(levels)
+    pos_sat[0:center] = [i/center for i in range(center)]
+    neg_sat = np.zeros(levels)
+    neg_sat[center+1:] = [(i+1)/center for i in range(center)]
+    print pos_sat
+    print neg_sat
+    # example for 5 levels (from black to color to white):
+    # [ 0.   0.5  1.   1.   1. ]
+    # [ 0.   0.   0.   0.5  1. ]
+
+    for i in range(levels):
+        print i + 1
+        inter_points = np.linspace(i/levels, (i+1)/levels, 5)  # interval points
+        print inter_points
+
+        CDICT = {'red': [(0.00, 1.0, 0.0),
+                         (0.25, 1.0, 1.0),
+                         (0.50, 1.0, 1.0),
+                         (0.75, 0.0, 0.0),
+                         (1.00, 0.0, 1.0)],
+
+             'green': [(0.00, 0.0, 0.0),
+                       (0.25, 0.0, 0.0),
+                       (0.50, 1.0, 1.0),
+                       (0.75, 1.0, 1.0),
+                       (1.00, 0.0, 1.0)],
+
+             'blue': [(0.00, 1.0, 1.0),
+                      (0.25, 0.0, 0.0),
+                      (0.50, 0.0, 0.0),
+                      (0.75, 0.0, 0.0),
+                      (1.00, 1.0, 1.0)]}
+
+        r.append((inter_points[0], 0, neg_sat[i]))
+        r.append((inter_points[1], pos_sat[i], pos_sat[i]))
+        r.append((inter_points[2], pos_sat[i], pos_sat[i]))
+        r.append((inter_points[3], neg_sat[i], neg_sat[i]))
+        r.append((inter_points[4], neg_sat[i], 0))
+        print r
+
+        g.append((inter_points[0], 0, neg_sat[i]))
+        g.append((inter_points[1], neg_sat[i], neg_sat[i]))
+        g.append((inter_points[2], pos_sat[i], pos_sat[i]))
+        g.append((inter_points[3], pos_sat[i], pos_sat[i]))
+        g.append((inter_points[4], neg_sat[i], 0))
+        print g
+
+        b.append((inter_points[0], 0, pos_sat[i]))
+        b.append((inter_points[1], neg_sat[i], neg_sat[i]))
+        b.append((inter_points[2], neg_sat[i], neg_sat[i]))
+        b.append((inter_points[3], neg_sat[i], neg_sat[i]))
+        b.append((inter_points[4], pos_sat[i], 0))
+        print b
+
+#        r.append((inter_points[0], 0, 0))
+#        r.append((inter_points[1], pos_sat[i], pos_sat[i]))
+#
+#        g.append((inter_points[0], 0, 0))
+#        g.append((inter_points[1], neg_sat[i], neg_sat[i]))
+#
+#        b.append((inter_points[0], 0, 0))
+#        b.append((inter_points[1], neg_sat[i], neg_sat[i]))
+
+    cdict = {'red': r, 'green': g, 'blue': b}
+    return mpl.colors.LinearSegmentedColormap('custom_colormap', cdict, N)
+
+
+x = np.arange(0, np.pi, 0.1)
+y = np.arange(0, 2*np.pi, 0.1)
+X, Y = np.meshgrid(x, y)
+Z = np.cos(X) * np.sin(Y) * 10
+
+fig = plt.figure(figsize=(7, 7))
+axis = fig.add_subplot(1, 1, 1)
+im = axis.imshow(Z, cmap=create_custom_colormap(levels=3, N=3))
+fig.colorbar(im)
diff --git a/scripts/temp/quiver_plot_enhanced.py b/scripts/temp/quiver_plot_enhanced.py
new file mode 100644
index 0000000..a30deae
--- /dev/null
+++ b/scripts/temp/quiver_plot_enhanced.py
@@ -0,0 +1,127 @@
+# -*- coding: utf-8 -*-
+"""
+Created on Fri Jun 19 13:26:32 2015
+
+@author: Jan
+"""
+
+
+from mayavi import mlab
+import numpy as np
+import pyramid as py
+
+
+mag_data = py.MagData.load_from_netcdf4('magdata_mc_orthogonal_thin_vortices.nc')
+#mag_data = py.MagData(1, np.ones((3, 2, 2, 2)))
+a = mag_data.a
+dim = mag_data.dim
+magnitude = mag_data.magnitude
+ad = 1
+limit = 1
+
+byte = 256
+
+def create_8bit_rgb_lut():
+    xl = np.mgrid[0:byte, 0:byte, 0:byte]
+    lut = np.vstack((xl[0].reshape(1, byte**3),
+                     xl[1].reshape(1, byte**3),
+                     xl[2].reshape(1, byte**3),
+                     255 * np.ones((1, byte**3)))).T
+    return lut.astype('int32')
+
+
+# indexing function to above grid
+def rgb_2_scalar_idx(r, g, b):
+    print 'r:', r, 'g:', g, 'b:', b
+    return byte**2 * r + byte * g + b
+
+# N x 3 colors
+colors = np.arange(3*np.prod(dim)).reshape((-1, 3)) % byte
+print colors
+
+# N scalars
+scalars = np.zeros(np.prod(dim))
+for (kp_idx, kp_c) in enumerate(colors):
+    print kp_idx, kp_c
+    scalars[kp_idx] = rgb_2_scalar_idx(kp_c[0], kp_c[1], kp_c[2])
+print scalars
+
+#rgb_lut = create_8bit_rgb_lut()
+
+# Create points and vector components as lists:
+zz, yy, xx = (np.indices(dim)-a/2).reshape((3,)+dim)
+zz = zz[::ad, ::ad, ::ad].flatten()
+yy = yy[::ad, ::ad, ::ad].flatten()
+xx = xx[::ad, ::ad, ::ad].flatten()
+x_mag = magnitude[0][::ad, ::ad, ::ad].flatten()
+y_mag = magnitude[1][::ad, ::ad, ::ad].flatten()
+z_mag = magnitude[2][::ad, ::ad, ::ad].flatten()
+# Plot them as vectors:
+mlab.figure(size=(750, 700))
+
+phis = (1 - np.arctan2(y_mag, x_mag)/np.pi) / 2
+thetas = np.arctan2(np.hypot(y_mag, x_mag), z_mag)/np.pi
+
+
+levels = 15
+N = 256
+cmap = py.MagData._create_directional_colormap(levels, N)
+
+
+def angle_to_rgba(phi, theta):
+    level = np.floor((1-theta) * levels)
+    lookup_value = (level + phi) / levels
+    rgba = cmap(lookup_value)
+    return tuple((np.asarray(rgba)*255).astype(np.int))
+
+colors = []
+for i in range(len(x_mag)):
+    colors.append(angle_to_rgba(phis[i], thetas[i]))
+
+plot = mlab.quiver3d(xx, yy, zz, x_mag, y_mag, z_mag, mode='arrow', colormap='jet')
+from tvtk.api import tvtk
+sc=tvtk.UnsignedCharArray()
+sc.from_array(colors)
+
+plot.mlab_source.dataset.point_data.scalars=colors
+plot.glyph.color_mode = 'color_by_scalar'
+plot.mlab_source.dataset.modified()
+
+
+#plot = mlab.quiver3d(xx, yy, zz, x_mag, y_mag, z_mag, mode='arrow',
+#                     colormap='jet')#py.MagData._create_directional_colormap())
+#angles = (1 - np.arctan2(y_mag, x_mag)/np.pi) / 2
+#plot.mlab_source.dataset.point_data.scalars = angles
+#plot.glyph.color_mode = 'color_by_scalar'
+#plot.glyph.glyph_source.glyph_position = 'center'
+#plot.module_manager.vector_lut_manager.data_range = np.array([0, limit])
+mlab.outline(plot)
+mlab.axes(plot)
+mlab.title('Title', height=0.95, size=0.35)
+#mlab.colorbar(label_fmt='%.2f')
+#mlab.colorbar(orientation='vertical')
+mlab.orientation_axes()
+mlab.show_pipeline()
+
+
+
+## magic to modify lookup table
+#plot.module_manager.scalar_lut_manager.lut._vtk_obj.SetTableRange(0, rgb_lut.shape[0])
+#plot.module_manager.scalar_lut_manager.lut.number_of_colors = rgb_lut.shape[0]
+#plot.module_manager.scalar_lut_manager.lut.table = rgb_lut
+
+
+#color = np.linspace(0, 256, np.prod(dim)).reshape(dim)
+#colors = np.asarray((color, color, color))
+
+#nr_points = 6
+#x,y,z=np.random.random((3,nr_points)) #some data
+#colors=np.random.randint(256,size=(nr_points,3)) #some RGB or RGBA colors
+#
+#pts=mlab.points3d(x,y,z)
+#sc=tvtk.UnsignedCharArray()
+#sc.from_array(colors)
+#
+#
+#pts.mlab_source.dataset.point_data.scalars=sc
+#pts.mlab_source.dataset.modified()
\ No newline at end of file
diff --git a/tests/test_compliance.py b/tests/test_compliance.py
index 077de64..aa94d2f 100644
--- a/tests/test_compliance.py
+++ b/tests/test_compliance.py
@@ -65,7 +65,7 @@ class TestCaseCompliance(unittest.TestCase):
         sys.stdout = stdout_buffer
         error_message = 'Found {} PEP8 violations!'.format(result.total_errors)
         if todo_count > 0:
-            error_message += 'Found {} TODOs!'.format(todo_count)
+            error_message += ' Found {} TODOs!'.format(todo_count)
         self.assertEqual(result.total_errors, 0, error_message)
 
 
diff --git a/tests/test_costfunction.py b/tests/test_costfunction.py
index 31034bf..a733e05 100644
--- a/tests/test_costfunction.py
+++ b/tests/test_costfunction.py
@@ -9,6 +9,7 @@ import numpy as np
 from numpy.testing import assert_allclose
 
 from pyramid.costfunction import Costfunction
+from pyramid.forwardmodel import ForwardModel
 from pyramid.dataset import DataSet
 from pyramid.projector import SimpleProjector
 from pyramid.phasemap import PhaseMap
@@ -29,7 +30,7 @@ class TestCaseCostfunction(unittest.TestCase):
         self.data.append(self.phase_map, self.projector)
         self.data.append(self.phase_map, self.projector)
         self.reg = FirstOrderRegularisator(self.mask, lam=1E-4)
-        self.cost = Costfunction(self.data, self.reg)
+        self.cost = Costfunction(ForwardModel(self.data), self.reg)
 
     def tearDown(self):
         self.path = None
diff --git a/tests/test_regularisator.py b/tests/test_regularisator.py
index 1a5040a..39ef31c 100644
--- a/tests/test_regularisator.py
+++ b/tests/test_regularisator.py
@@ -129,8 +129,9 @@ class TestCaseFirstOrderRegularisator(unittest.TestCase):
 
     def test_hess_diag(self):
         hess_diag = self.reg.hess_diag(np.ones(self.n))
-        hess_diag_ref = np.diag(np.load(os.path.join(self.path, 'first_order_jac_ref.npy')))
-        print hess_diag_ref
+        hess_diag_ref = np.zeros(3*self.n)  # derivatives in all directions!
+        first_order_jac_ref = np.load(os.path.join(self.path, 'first_order_jac_ref.npy'))
+        hess_diag_ref[0:self.n] = np.diag(first_order_jac_ref)
         assert_allclose(hess_diag, hess_diag_ref, atol=1E-7,
                         err_msg='Unexpected behaviour in hess_diag()!')
 
-- 
GitLab