From af849c722b294d79f5dff2bbb1ae5b5d65c77ed7 Mon Sep 17 00:00:00 2001
From: Jan Caron <j.caron@fz-juelich.de>
Date: Sat, 7 Mar 2015 21:58:19 +0100
Subject: [PATCH] =?UTF-8?q?Convenience=20functions=20and=20bug=20fixes!=20?=
 =?UTF-8?q?magdata:=20added=20convenience=20functions=20for=20flipping=20a?=
 =?UTF-8?q?nd=20rotating=20of=20magnetic=20=20=20=20=20=20=20=20=20=20dist?=
 =?UTF-8?q?ributions,=20fixed=20bug=20with=20ar=5Fdens.=20phasemap:=20now?=
 =?UTF-8?q?=20also=20has=20a=20mask=20which=20will=20be=20used=20to=20cons?=
 =?UTF-8?q?truct=20a=203D-mask=20for=20the=20=20=20=20=20=20=20=20=20=20?=
 =?UTF-8?q?=20magnetic=20distribution=20during=20reconstruction.=20Added?=
 =?UTF-8?q?=20setter/getter.=20=20=20=20=20=20=20=20=20=20=20Added=20copy(?=
 =?UTF-8?q?),=20scale=5Fdown()/scale=5Fup()=20convenience=20functions.=20?=
 =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20!CAUTION!=20Only=20NetCDF4=20file?=
 =?UTF-8?q?s=20save=20every=20new=20parameter!=20projector:=20Fixed=20hidd?=
 =?UTF-8?q?en=20bug=20in=20X/YTiltProjector,=20which=20was=20problematic?=
 =?UTF-8?q?=20during=20=20=20=20=20=20=20=20=20=20=20=20tilts=20over=20360?=
 =?UTF-8?q?=B0.=20Now=20uses=20linear=20algebra=20(much=20clearer!).=20=20?=
 =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20get=5Finfo()=20function=20now=20c?=
 =?UTF-8?q?an=20be=20used=20non-verbose=20(for=20filenames).=20dataset:=20?=
 =?UTF-8?q?added=20several=20setter/getter,=20addes=20set=5F3d=5Fmask()=20?=
 =?UTF-8?q?for=20simple=202D=20tasks,=20=20=20=20=20=20=20=20=20=20renamed?=
 =?UTF-8?q?=20mask=20to=20confidence=20(for=20Se=5Finv=20construction)=20t?=
 =?UTF-8?q?ests:=20updated=20to=20include=20the=20fixes=20and=20new=20func?=
 =?UTF-8?q?tions=20introduced=20in=20this=20update!?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 pyramid/config.py                        |   6 +-
 pyramid/costfunction.py                  |   7 +-
 pyramid/dataset.py                       |  74 +++++++--
 pyramid/magdata.py                       | 113 ++++++++++---
 pyramid/phasemap.py                      | 197 +++++++++++++++++++----
 pyramid/projector.py                     |  75 +++++----
 pyramid/version.py                       |   2 +-
 scripts/gui/mag_slicer.py                |   5 +-
 tests/test_costfunction/phase_map_ref.nc | Bin 6312 -> 6940 bytes
 tests/test_dataset.py                    |  10 +-
 tests/test_forwardmodel/phase_map_ref.nc | Bin 6312 -> 6940 bytes
 tests/test_magdata.py                    |  28 +++-
 tests/test_magdata/mag_data_flipx.nc     | Bin 0 -> 9436 bytes
 tests/test_magdata/mag_data_flipy.nc     | Bin 0 -> 9436 bytes
 tests/test_magdata/mag_data_flipz.nc     | Bin 0 -> 9436 bytes
 tests/test_magdata/mag_data_orig.nc      | Bin 0 -> 9436 bytes
 tests/test_magdata/mag_data_ref_load.nc  | Bin 0 -> 7684 bytes
 tests/test_magdata/mag_data_ref_load.txt |  66 ++++++++
 tests/test_magdata/mag_data_rotx.nc      | Bin 0 -> 9436 bytes
 tests/test_magdata/mag_data_roty.nc      | Bin 0 -> 9436 bytes
 tests/test_magdata/mag_data_rotz.nc      | Bin 0 -> 9436 bytes
 tests/test_phasemap.py                   |  43 ++++-
 tests/test_phasemap/ref_phase_map.nc     | Bin 6256 -> 6814 bytes
 tests/test_phasemapper/phase_map.nc      | Bin 6256 -> 6814 bytes
 tests/test_phasemapper/phase_map_fc.nc   | Bin 6256 -> 6814 bytes
 tests/test_projector.py                  |   8 +-
 tests/test_projector/ref_mag_proj_x.nc   | Bin 7276 -> 7276 bytes
 27 files changed, 516 insertions(+), 118 deletions(-)
 create mode 100644 tests/test_magdata/mag_data_flipx.nc
 create mode 100644 tests/test_magdata/mag_data_flipy.nc
 create mode 100644 tests/test_magdata/mag_data_flipz.nc
 create mode 100644 tests/test_magdata/mag_data_orig.nc
 create mode 100644 tests/test_magdata/mag_data_ref_load.nc
 create mode 100644 tests/test_magdata/mag_data_ref_load.txt
 create mode 100644 tests/test_magdata/mag_data_rotx.nc
 create mode 100644 tests/test_magdata/mag_data_roty.nc
 create mode 100644 tests/test_magdata/mag_data_rotz.nc

diff --git a/pyramid/config.py b/pyramid/config.py
index 9f45e1f..c0d9db4 100644
--- a/pyramid/config.py
+++ b/pyramid/config.py
@@ -8,11 +8,11 @@ Created on Fri Feb 20 10:59:42 2015
 import os
 
 
-__all__ = ['DIR_PACKAGE', 'DIR_MAGDATA', 'DIR_PHASEMAP']
+__all__ = ['DIR_PACKAGE', 'DIR_FILES', 'LOGGING_CONFIG']
 
 
 DIR_PACKAGE = os.path.join(os.path.dirname(os.path.realpath(__file__)))
-DIR_MAGDATA = os.path.abspath(os.path.join(DIR_PACKAGE, os.pardir, 'files', 'magdata'))
-DIR_PHASEMAP = os.path.abspath(os.path.join(DIR_PACKAGE, os.pardir, 'files', 'phasemap'))
+DIR_FILES = os.path.abspath(os.path.join(DIR_PACKAGE, os.pardir, 'files'))
+LOGGING_CONFIG = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'logging.ini')
 
 del os
diff --git a/pyramid/costfunction.py b/pyramid/costfunction.py
index d7eae39..7b3945a 100644
--- a/pyramid/costfunction.py
+++ b/pyramid/costfunction.py
@@ -64,10 +64,9 @@ class Costfunction(object):
         self.y = data_set.phase_vec
         self.n = data_set.n
         self.m = data_set.m
-        if data_set.Se_inv is not None:
-            self.Se_inv = data_set.Se_inv
-        else:
-            self.Se_inv = sparse_eye(self.m)
+        if data_set.Se_inv is None:
+            data_set.set_Se_inv_diag_with_conf()
+        self.Se_inv = data_set.Se_inv
         self._log.debug('Created '+str(self))
 
     def __repr__(self):
diff --git a/pyramid/dataset.py b/pyramid/dataset.py
index ca32225..f493b05 100644
--- a/pyramid/dataset.py
+++ b/pyramid/dataset.py
@@ -64,10 +64,36 @@ class DataSet(object):
 
     _log = logging.getLogger(__name__+'.DataSet')
 
+    @property
+    def a(self):
+        return self._a
+
+    @a.setter
+    def a(self, a):
+        assert isinstance(a, Number), 'Grid spacing has to be a number!'
+        assert a >= 0, 'Grid spacing has to be a positive number!'
+        self._a = float(a)
+
+    @property
+    def mask(self):
+        return self._mask
+
+    @mask.setter
+    def mask(self, mask):
+        if mask is not None:
+            assert mask.shape == self.dim, 'Mask dimensions must match!'
+        else:
+            mask = np.ones(self.dim, dtype=bool)
+        self._mask = mask.astype(np.bool)
+
     @property
     def m(self):
         return np.sum([len(p.phase_vec) for p in self.phase_maps])
 
+    @property
+    def n(self):
+        return 3 * np.sum(self.mask)
+
     @property
     def phase_vec(self):
         return np.concatenate([p.phase_vec for p in self.phase_maps])
@@ -87,19 +113,12 @@ class DataSet(object):
 
     def __init__(self, a, dim, b_0=1, mask=None, Se_inv=None):
         self._log.debug('Calling __init__')
-        assert isinstance(a, Number), 'Grid spacing has to be a number!'
-        assert a >= 0, 'Grid spacing has to be a positive number!'
         assert isinstance(dim, tuple) and len(dim) == 3, \
             'Dimension has to be a tuple of length 3!'
-        if mask is None:
-            self.mask = np.ones(dim, dtype=bool)
-        else:
-            assert mask.shape == dim, 'Mask dimensions must match!'
-            self.mask = mask
-        self.n = 3 * np.sum(self.mask)
         self.a = a
         self.dim = dim
         self.b_0 = b_0
+        self.mask = mask
         self.Se_inv = Se_inv
         self.phase_maps = []
         self.projectors = []
@@ -167,27 +186,50 @@ class DataSet(object):
             None
 
         '''
+        assert len(cov_list) == len(self.phase_maps), 'Needs one covariance matrix per phase map!'
         self.Se_inv = sparse.block_diag(cov_list).tocsr()
 
-    def set_Se_inv_diag_with_masks(self, mask_list):
-        '''Set the Se_inv matrix as a block diagonal matrix from a list of masks
+    def set_Se_inv_diag_with_conf(self, conf_list=None):
+        '''Set the Se_inv matrix as a block diagonal matrix from a list of confidence matrizes.
 
         Parameters
         ----------
-        mask_list: list of :class:`~numpy.ndarray`
-            List of 2D masks (one for each projection) which define trust regions.
+        conf_list: list of :class:`~numpy.ndarray` (optional)
+            List of 2D confidence matrizes (one for each projection) which define trust regions.
+            If not given this uses the confidence matrizes of the phase maps.
 
         Returns
         -------
             None
 
         '''
-        cov_list = [sparse.diags(m.flatten().astype(np.float32), 0) for m in mask_list]
+        if conf_list is None:  # if no confidence matrizes are given, extract from the phase maps!
+            conf_list = [phase_map.confidence for phase_map in self.phase_maps]
+        cov_list = [sparse.diags(c.flatten().astype(np.float32), 0) for c in conf_list]
         self.set_Se_inv_block_diag(cov_list)
 
-    def create_3d_mask(self, mask_list):
-        # TODO: method for constructing 3D mask from 2D masks?
-        raise NotImplementedError()
+    def set_3d_mask(self, mask_list=None):
+        '''Set the 3D mask from a list of 2D masks.
+
+        Parameters
+        ----------
+        mask_list: list of :class:`~numpy.ndarray` (optional)
+            List of 2D masks, which represent the projections of the 3D mask. If not given this
+            uses the confidence matrizes of the phase maps. If just one phase map is present, the
+            according mask is simply expanded to 3D and used directly.
+
+        Returns
+        -------
+            None
+
+        '''
+        if mask_list is None:  # if no masks are given, extract from phase maps:
+            mask_list = [phase_map.mask for phase_map in self.phase_maps]
+        if len(mask_list) == 1:  # just one phase_map --> 3D mask equals 2D mask
+            self.mask = np.expand_dims(mask_list[0], axis=0)
+        else:  # 3D mask has to be constructed from 2D masks:
+            # TODO: method for constructing 3D mask from 2D masks? if no list use phase_map masks!
+            raise NotImplementedError()
 
     def display_phase(self, mag_data=None, title='Phase Map',
                       cmap='RdBu', limit=None, norm=None):
diff --git a/pyramid/magdata.py b/pyramid/magdata.py
index 936e4e5..f2f9fb4 100644
--- a/pyramid/magdata.py
+++ b/pyramid/magdata.py
@@ -331,6 +331,69 @@ class MagData(object):
         else:
             self.mag_vec = vector
 
+    def flip(self, axis='x'):
+        '''Flip/mirror the magnetization around the specified axis.
+
+        Parameters
+        ----------
+        axis: {'x', 'y', 'z'}, optional
+            The axis around which the magnetization is flipped.
+
+        Returns
+        -------
+        mag_data_flip: :class:`~.MagData`
+           A flipped copy of the :class:`~.MagData` object.
+
+        '''
+        if axis == 'x':
+            mag_x, mag_y, mag_z = self.magnitude[:, :, :, ::-1]
+            magnitude_flip = np.array((-mag_x, mag_y, mag_z))
+        elif axis == 'y':
+            mag_x, mag_y, mag_z = self.magnitude[:, :, ::-1, :]
+            magnitude_flip = np.array((mag_x, -mag_y, mag_z))
+        elif axis == 'z':
+            mag_x, mag_y, mag_z = self.magnitude[:, ::-1, :, :]
+            magnitude_flip = np.array((mag_x, mag_y, -mag_z))
+        else:
+            raise ValueError("Wrong input! 'x', 'y', 'z' allowed!")
+        return MagData(self.a, magnitude_flip)
+
+    def rot90(self, axis='x'):
+        '''Rotate the magnetization 90° around the specified axis (right hand rotation).
+
+        Parameters
+        ----------
+        axis: {'x', 'y', 'z'}, optional
+            The axis around which the magnetization is rotated.
+
+        Returns
+        -------
+        mag_data_rot: :class:`~.MagData`
+           A rotated copy of the :class:`~.MagData` object.
+
+        '''
+        if axis == 'x':
+            magnitude_rot = np.zeros((3, self.dim[1], self.dim[0], self.dim[2]))
+            for i in range(self.dim[2]):
+                mag_x, mag_y, mag_z = self.magnitude[:, :, :, i]
+                mag_xrot, mag_yrot, mag_zrot = np.rot90(mag_x), np.rot90(mag_y), np.rot90(mag_z)
+                magnitude_rot[:, :, :, i] = np.array((mag_xrot, mag_zrot, -mag_yrot))
+        elif axis == 'y':
+            magnitude_rot = np.zeros((3, self.dim[2], self.dim[1], self.dim[0]))
+            for i in range(self.dim[1]):
+                mag_x, mag_y, mag_z = self.magnitude[:, :, i, :]
+                mag_xrot, mag_yrot, mag_zrot = np.rot90(mag_x), np.rot90(mag_y), np.rot90(mag_z)
+                magnitude_rot[:, :, i, :] = np.array((mag_zrot, mag_yrot, -mag_xrot))
+        elif axis == 'z':
+            magnitude_rot = np.zeros((3, self.dim[0], self.dim[2], self.dim[1]))
+            for i in range(self.dim[0]):
+                mag_x, mag_y, mag_z = self.magnitude[:, i, :, :]
+                mag_xrot, mag_yrot, mag_zrot = np.rot90(mag_x), np.rot90(mag_y), np.rot90(mag_z)
+                magnitude_rot[:, i, :, :] = np.array((mag_yrot, -mag_xrot, mag_zrot))
+        else:
+            raise ValueError("Wrong input! 'x', 'y', 'z' allowed!")
+        return MagData(self.a, magnitude_rot)
+
     def save_to_llg(self, filename='magdata.txt'):
         '''Save magnetization data in a file with LLG-format.
 
@@ -353,10 +416,11 @@ class MagData(object):
         data = np.array([xx, yy, zz, x_vec, y_vec, z_vec]).T
         # Construct path if filename isn't already absolute:
         if not os.path.isabs(filename):
-            from pyramid import DIR_MAGDATA
-            if not os.path.exists(DIR_MAGDATA):
-                os.makedirs(DIR_MAGDATA)
-            filename = os.path.join(DIR_MAGDATA, filename)
+            from pyramid import DIR_FILES
+            directory = os.path.join(DIR_FILES, 'magdata')
+            if not os.path.exists(directory):
+                os.makedirs(directory)
+            filename = os.path.join(directory, filename)
         # Save data to file:
         with open(filename, 'w') as mag_file:
             mag_file.write('LLGFileCreator: %s\n' % filename)
@@ -383,10 +447,11 @@ class MagData(object):
         SCALE = 1.0E-9 / 1.0E-2  # From cm to nm
         # Construct path if filename isn't already absolute:
         if not os.path.isabs(filename):
-            from pyramid import DIR_MAGDATA
-            if not os.path.exists(DIR_MAGDATA):
-                os.makedirs(DIR_MAGDATA)
-            filename = os.path.join(DIR_MAGDATA, filename)
+            from pyramid import DIR_FILES
+            directory = os.path.join(DIR_FILES, 'magdata')
+            if not os.path.exists(directory):
+                os.makedirs(directory)
+            filename = os.path.join(directory, filename)
         # Load data from file:
         data = np.genfromtxt(filename, skip_header=2)
         dim = tuple(np.genfromtxt(filename, dtype=int, skip_header=1, skip_footer=len(data[:, 0])))
@@ -411,10 +476,11 @@ class MagData(object):
         self._log.debug('Calling save_to_netcdf4')
         # Construct path if filename isn't already absolute:
         if not os.path.isabs(filename):
-            from pyramid import DIR_MAGDATA
-            if not os.path.exists(DIR_MAGDATA):
-                os.makedirs(DIR_MAGDATA)
-            filename = os.path.join(DIR_MAGDATA, filename)
+            from pyramid import DIR_FILES
+            directory = os.path.join(DIR_FILES, 'magdata')
+            if not os.path.exists(directory):
+                os.makedirs(directory)
+            filename = os.path.join(directory, filename)
         # Save data to file:
         mag_file = netCDF4.Dataset(filename, 'w', format='NETCDF4')
         mag_file.a = self.a
@@ -444,10 +510,11 @@ class MagData(object):
         cls._log.debug('Calling load_from_netcdf4')
         # Construct path if filename isn't already absolute:
         if not os.path.isabs(filename):
-            from pyramid import DIR_MAGDATA
-            if not os.path.exists(DIR_MAGDATA):
-                os.makedirs(DIR_MAGDATA)
-            filename = os.path.join(DIR_MAGDATA, filename)
+            from pyramid import DIR_FILES
+            directory = os.path.join(DIR_FILES, 'magdata')
+            if not os.path.exists(directory):
+                os.makedirs(directory)
+            filename = os.path.join(directory, filename)
         # Load data from file:
         mag_file = netCDF4.Dataset(filename, 'r', format='NETCDF4')
         a = mag_file.a
@@ -511,10 +578,11 @@ class MagData(object):
                                  value='{} {} {}'.format(*spin_scale))
         # Construct path if filename isn't already absolute:
         if not os.path.isabs(filename):
-            from pyramid import DIR_MAGDATA
-            if not os.path.exists(DIR_MAGDATA):
-                os.makedirs(DIR_MAGDATA)
-            filename = os.path.join(DIR_MAGDATA, filename)
+            from pyramid import DIR_FILES
+            directory = os.path.join(DIR_FILES, 'x3d')
+            if not os.path.exists(directory):
+                os.makedirs(directory)
+            filename = os.path.join(directory, filename)
         # Write the tree into the file in pretty print format:
         tree.write(filename, pretty_print=True)
 
@@ -650,7 +718,10 @@ class MagData(object):
             limit = np.max(self.mag_amp)
         ad = ar_dens
         # Create points and vector components as lists:
-        zz, yy, xx = (np.indices(dim)-a/2).reshape(3, -1)
+        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 = self.magnitude[0][::ad, ::ad, ::ad].flatten()
         y_mag = self.magnitude[1][::ad, ::ad, ::ad].flatten()
         z_mag = self.magnitude[2][::ad, ::ad, ::ad].flatten()
diff --git a/pyramid/phasemap.py b/pyramid/phasemap.py
index 047c275..9dddb72 100644
--- a/pyramid/phasemap.py
+++ b/pyramid/phasemap.py
@@ -9,6 +9,7 @@ import os
 
 import numpy as np
 from numpy import pi
+from scipy.ndimage.interpolation import zoom
 
 import matplotlib as mpl
 import matplotlib.pyplot as plt
@@ -52,6 +53,13 @@ class PhaseMap(object):
         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
@@ -126,7 +134,7 @@ class PhaseMap(object):
     def phase(self, phase):
         assert isinstance(phase, np.ndarray), 'Phase has to be a numpy array!'
         assert len(phase.shape) == 2, 'Phase has to be 2-dimensional!'
-        self._phase = np.asarray(phase, dtype=np.float32)
+        self._phase = phase.astype(dtype=np.float32)
         self._dim_uv = phase.shape
 
     @property
@@ -139,6 +147,31 @@ class PhaseMap(object):
         assert np.size(phase_vec) == np.prod(self.dim_uv), 'Vector size has to match phase!'
         self.phase = phase_vec.reshape(self.dim_uv)
 
+    @property
+    def mask(self):
+        return self._mask
+
+    @mask.setter
+    def mask(self, mask):
+        if mask is not None:
+            assert mask.shape == self.phase.shape, 'Mask and phase dimensions must match!!'
+        else:
+            mask = np.ones_like(self.phase, dtype=bool)
+        self._mask = mask.astype(np.bool)
+
+    @property
+    def confidence(self):
+        return self._confidence
+
+    @confidence.setter
+    def confidence(self, confidence):
+        if confidence is not None:
+            assert confidence.shape == self.phase.shape, \
+                'Confidence and phase dimensions must match!'
+        else:
+            confidence = np.ones_like(self.phase)
+        self._confidence = confidence.astype(dtype=np.float32)
+
     @property
     def unit(self):
         return self._unit
@@ -148,25 +181,27 @@ class PhaseMap(object):
         assert unit in self.UNITDICT, 'Unit not supported!'
         self._unit = unit
 
-    def __init__(self, a, phase, unit='rad'):
+    def __init__(self, a, phase, mask=None, confidence=None, unit='rad'):
         self._log.debug('Calling __init__')
         self.a = a
         self.phase = phase
+        self.mask = mask
+        self.confidence = confidence
         self.unit = unit
         self._log.debug('Created '+str(self))
 
     def __repr__(self):
         self._log.debug('Calling __repr__')
-        return '%s(a=%r, phase=%r, unit=%r)' % \
-            (self.__class__, self.a, self.phase, self.unit)
+        return '%s(a=%r, phase=%r, mask=%r, confidence=%r, unit=%r)' % \
+            (self.__class__, self.a, self.phase, self.mask, self.confidence, self.unit)
 
     def __str__(self):
         self._log.debug('Calling __str__')
-        return 'PhaseMap(a=%s, dim_uv=%s)' % (self.a, self.dim_uv)
+        return 'PhaseMap(a=%s, dim_uv=%s, mask=%s)' % (self.a, self.dim_uv, not np.all(self.mask))
 
     def __neg__(self):  # -self
         self._log.debug('Calling __neg__')
-        return PhaseMap(self.a, -self.phase, self.unit)
+        return PhaseMap(self.a, -self.phase, self.mask, self.confidence, self.unit)
 
     def __add__(self, other):  # self + other
         self._log.debug('Calling __add__')
@@ -177,10 +212,11 @@ class PhaseMap(object):
             assert other.a == self.a, 'Added phase has to have the same grid spacing!'
             assert other.phase.shape == self.dim_uv, \
                 'Added magnitude has to have the same dimensions!'
-            return PhaseMap(self.a, self.phase+other.phase, self.unit)
+            mask_comb = np.logical_or(self.mask, other.mask)  # masks combine, confidence resets!
+            return PhaseMap(self.a, self.phase+other.phase, mask_comb, None, self.unit)
         else:  # other is a Number
             self._log.debug('Adding an offset')
-            return PhaseMap(self.a, self.phase+other, self.unit)
+            return PhaseMap(self.a, self.phase+other, self.mask, self.confidence, self.unit)
 
     def __sub__(self, other):  # self - other
         self._log.debug('Calling __sub__')
@@ -191,7 +227,7 @@ class PhaseMap(object):
         assert (isinstance(other, Number)
                 or (isinstance(other, np.ndarray) and other.shape == self.dim_uv)), \
             'PhaseMap objects can only be multiplied by scalar numbers or fitting arrays!'
-        return PhaseMap(self.a, other*self.phase, self.unit)
+        return PhaseMap(self.a, other*self.phase, self.mask, self.confidence, self.unit)
 
     def __radd__(self, other):  # other + self
         self._log.debug('Calling __radd__')
@@ -217,6 +253,86 @@ class PhaseMap(object):
         self._log.debug('Calling __imul__')
         return self.__mul__(other)
 
+    def copy(self):
+        '''Returns a copy of the :class:`~.PhaseMap` object
+
+        Parameters
+        ----------
+        None
+
+        Returns
+        -------
+        phase_map: :class:`~.PhaseMap`
+            A copy of the :class:`~.PhaseMap`.
+
+        '''
+        self._log.debug('Calling copy')
+        return PhaseMap(self.a, self.phase.copy(), self.mask.copy(),
+                        self.confidence.copy(), self.unit)
+
+    def scale_down(self, n=1):
+        '''Scale down the phase map by averaging over two pixels along each axis.
+
+        Parameters
+        ----------
+        n : int, optional
+            Number of times the phase map is scaled down. The default is 1.
+
+        Returns
+        -------
+        None
+
+        Notes
+        -----
+        Acts in place and changes dimensions and grid spacing accordingly.
+        Only possible, if each axis length is a power of 2!
+
+        '''
+        self._log.debug('Calling scale_down')
+        assert n > 0 and isinstance(n, (int, long)), 'n must be a positive integer!'
+        self.a = self.a * 2**n
+        for t in range(n):
+            # Pad if necessary:
+            pv, pu = self.dim_uv[0] % 2, self.dim_uv[1] % 2
+            if pv != 0 or pu != 0:
+                self.phase = np.pad(self.phase, ((0, pv), (0, pu)), mode='constant')
+            # Create coarser grid for the magnetization:
+            dim_uv = self.dim_uv
+            self.phase = self.phase.reshape(dim_uv[0]/2, 2, dim_uv[1]/2, 2).mean(axis=(3, 1))
+            mask = self.mask.reshape(dim_uv[0]/2, 2, dim_uv[1]/2, 2)
+            self.mask = mask[:, 0, :, 0] & mask[:, 1, :, 0] & mask[:, 0, :, 1] & mask[:, 1, :, 1]
+            self.confidence = self.confidence.reshape(dim_uv[0]/2, 2,
+                                                      dim_uv[1]/2, 2).mean(axis=(3, 1))
+
+    def scale_up(self, n=1, order=0):
+        '''Scale up the phase map using spline interpolation of the requested order.
+
+        Parameters
+        ----------
+        n : int, optional
+            Power of 2 with which the grid is scaled. Default is 1, which means every axis is
+            increased by a factor of ``2**1 = 2``.
+        order : int, optional
+            The order of the spline interpolation, which has to be in the range between 0 and 5
+            and defaults to 0.
+
+        Returns
+        -------
+        None
+
+        Notes
+        -----
+        Acts in place and changes dimensions and grid spacing accordingly.
+        '''
+        self._log.debug('Calling scale_up')
+        assert n > 0 and isinstance(n, (int, long)), 'n must be a positive integer!'
+        assert 5 > order >= 0 and isinstance(order, (int, long)), \
+            'order must be a positive integer between 0 and 5!'
+        self.a = self.a / 2**n
+        self.phase = zoom(self.phase, zoom=2**n, order=order)
+        self.mask = zoom(self.mask, zoom=2**n, order=0)
+        self.confidence = zoom(self.confidence, zoom=2**n, order=order)
+
     def save_to_txt(self, filename='phasemap.txt'):
         '''Save :class:`~.PhaseMap` data in a file with txt-format.
 
@@ -234,10 +350,11 @@ class PhaseMap(object):
         self._log.debug('Calling save_to_txt')
         # Construct path if filename isn't already absolute:
         if not os.path.isabs(filename):
-            from pyramid import DIR_PHASEMAP
-            if not os.path.exists(DIR_PHASEMAP):
-                os.makedirs(DIR_PHASEMAP)
-            filename = os.path.join(DIR_PHASEMAP, filename)
+            from pyramid import DIR_FILES
+            directory = os.path.join(DIR_FILES, 'phasemap')
+            if not os.path.exists(directory):
+                os.makedirs(directory)
+            filename = os.path.join(directory, filename)
         # Save data to file:
         with open(filename, 'w') as phase_file:
             phase_file.write('{}\n'.format(filename.replace('.txt', '')))
@@ -258,14 +375,20 @@ class PhaseMap(object):
         phase_map : :class:`~.PhaseMap`
             A :class:`~.PhaseMap` object containing the loaded data.
 
+        Notes
+        -----
+        Does not recover the mask, confidence or unit of the original phase map, which default to
+        `None`, `None` and `'rad'`, respectively.
+
         '''
         cls._log.debug('Calling load_from_txt')
         # Construct path if filename isn't already absolute:
         if not os.path.isabs(filename):
-            from pyramid import DIR_PHASEMAP
-            if not os.path.exists(DIR_PHASEMAP):
-                os.makedirs(DIR_PHASEMAP)
-            filename = os.path.join(DIR_PHASEMAP, filename)
+            from pyramid import DIR_FILES
+            directory = os.path.join(DIR_FILES, 'phasemap')
+            if not os.path.exists(directory):
+                os.makedirs(directory)
+            filename = os.path.join(directory, filename)
         # Load data from file:
         with open(filename, 'r') as phase_file:
             phase_file.readline()  # Headerline is not used
@@ -286,21 +409,30 @@ class PhaseMap(object):
         -------
         None
 
+        Notes
+        -----
+        Does not save the unit of the original phase map.
+
         '''
         self._log.debug('Calling save_to_netcdf4')
         # Construct path if filename isn't already absolute:
         if not os.path.isabs(filename):
-            from pyramid import DIR_PHASEMAP
-            if not os.path.exists(DIR_PHASEMAP):
-                os.makedirs(DIR_PHASEMAP)
-            filename = os.path.join(DIR_PHASEMAP, filename)
+            from pyramid import DIR_FILES
+            directory = os.path.join(DIR_FILES, 'phasemap')
+            if not os.path.exists(directory):
+                os.makedirs(directory)
+            filename = os.path.join(directory, filename)
         # Save data to file:
         phase_file = netCDF4.Dataset(filename, 'w', format='NETCDF4')
         phase_file.a = self.a
         phase_file.createDimension('v_dim', self.dim_uv[0])
         phase_file.createDimension('u_dim', self.dim_uv[1])
         phase = phase_file.createVariable('phase', 'f', ('v_dim', 'u_dim'))
+        mask = phase_file.createVariable('mask', 'b', ('v_dim', 'u_dim'))
+        confidence = phase_file.createVariable('confidence', 'f', ('v_dim', 'u_dim'))
         phase[:] = self.phase
+        mask[:] = self.mask
+        confidence[:] = self.confidence
         phase_file.close()
 
     @classmethod
@@ -317,23 +449,30 @@ class PhaseMap(object):
         phase_map: :class:`~.PhaseMap`
             A :class:`~.PhaseMap` object containing the loaded data.
 
+        Notes
+        -----
+        Does not recover the unit of the original phase map, defaults to `'rad'`.
+
         '''
         cls._log.debug('Calling load_from_netcdf4')
         # Construct path if filename isn't already absolute:
         if not os.path.isabs(filename):
-            from pyramid import DIR_PHASEMAP
-            if not os.path.exists(DIR_PHASEMAP):
-                os.makedirs(DIR_PHASEMAP)
-            filename = os.path.join(DIR_PHASEMAP, filename)
+            from pyramid import DIR_FILES
+            directory = os.path.join(DIR_FILES, 'phasemap')
+            if not os.path.exists(directory):
+                os.makedirs(directory)
+            filename = os.path.join(directory, filename)
         # Load data from file:
         phase_file = netCDF4.Dataset(filename, 'r', format='NETCDF4')
         a = phase_file.a
         phase = phase_file.variables['phase'][:]
+        mask = phase_file.variables['mask'][:]
+        confidence = phase_file.variables['confidence'][:]
         phase_file.close()
-        return PhaseMap(a, phase)
+        return PhaseMap(a, phase, mask, confidence)
 
     def display_phase(self, title='Phase Map', cmap='RdBu', limit=None,
-                      norm=None, axis=None, cbar=True):
+                      norm=None, axis=None, cbar=True, show_mask=True):
         '''Display the phasemap as a colormesh.
 
         Parameters
@@ -372,6 +511,8 @@ class PhaseMap(object):
         axis.set_aspect('equal')
         # Plot the phasemap:
         im = axis.pcolormesh(phase, cmap=cmap, vmin=-limit, vmax=limit, norm=norm)
+        if show_mask and not np.all(self.mask):  # Plot mask if desired and not trivial!
+            axis.contour(self.mask, levels=[0.999999], colors='g')
         # Set the axes ticks and labels:
         axis.set_xlim(0, self.dim_uv[1])
         axis.set_ylim(0, self.dim_uv[0])
@@ -579,7 +720,7 @@ class PhaseMap(object):
         '''
         cls._log.debug('Calling make_color_wheel')
         yy, xx = np.indices((512, 512)) - 256
-        r = np.sqrt(xx ** 2 + yy ** 2)
+        r = np.sqrt(xx**2 + yy**2)
         # Create the wheel:
         color_wheel_magnitude = (1 - np.cos(r * pi/360)) / 2
         color_wheel_magnitude *= 0 * (r > 256) + 1 * (r <= 256)
diff --git a/pyramid/projector.py b/pyramid/projector.py
index a30b91f..ef845e9 100644
--- a/pyramid/projector.py
+++ b/pyramid/projector.py
@@ -184,12 +184,14 @@ class Projector(object):
                                  'vector- or scalar-field-projection!')
 
     @abc.abstractmethod
-    def get_info(self):
+    def get_info(self, verbose):
         '''Get specific information about the projector as a string.
 
         Parameters
         ----------
-        None
+        verbose: boolean, optional
+            If this is true, the text looks prettier (maybe using latex). Default is False for the
+            use in file names and such.
 
         Returns
         -------
@@ -223,9 +225,12 @@ class XTiltProjector(Projector):
 
     def __init__(self, dim, tilt, dim_uv=None):
 
-        def get_position(p, m, b, size):
-            y, x = np.array(p)[:, 0]+0.5, np.array(p)[:, 1]+0.5
-            return (y-m*x-b)/np.sqrt(m**2+1) + size/2.
+        def get_position(points, center, tilt, size):
+            point_vecs = np.asarray(points)+0.5 - np.asarray(center)  # vectors pointing to points
+            direc_vec = np.array((np.cos(tilt), -np.sin(tilt)))  # vector pointing along projection
+            distances = -np.cross(point_vecs, direc_vec)  # here (special case): divisor is one!
+            # minus because sign is derived of -sin(angle(point_vec, direc_vec)) neg between 0-180°
+            return distances + size/2.  # Shift to center the projection
 
         def get_impact(pos, r, size):
             return [x for x in np.arange(np.floor(pos-r), np.floor(pos+r)+1, dtype=int)
@@ -260,9 +265,7 @@ class XTiltProjector(Projector):
         voxels = list(itertools.product(range(dim_proj), range(dim_perp)))  # z-y-plane
         # Calculate positions along the projected pixel coordinate system:
         center = (dim_proj/2., dim_perp/2.)
-        m = np.where(tilt <= pi, -1/np.tan(tilt+1E-30), 1/np.tan(tilt+1E-30))
-        b = center[0] - m * center[1]
-        positions = get_position(voxels, m, b, dim_v)
+        positions = get_position(voxels, center, tilt, dim_v)
         # Calculate weight-matrix:
         r = 1/np.sqrt(np.pi)  # radius of the voxel circle
         rho = 0.5 / r
@@ -291,12 +294,14 @@ class XTiltProjector(Projector):
         super(XTiltProjector, self).__init__(dim, dim_uv, weight, coeff)
         self._log.debug('Created '+str(self))
 
-    def get_info(self):
+    def get_info(self, verbose=False):
         '''Get specific information about the projector as a string.
 
         Parameters
         ----------
-        None
+        verbose: boolean, optional
+            If this is true, the text looks prettier (maybe using latex). Default is False for the
+            use in file names and such.
 
         Returns
         -------
@@ -304,7 +309,10 @@ class XTiltProjector(Projector):
             Information about the projector as a string, e.g. for the use in plot titles.
 
         '''
-        return 'x-tilt: $\phi = {:3.2f} \pi$'.format(self.tilt/pi)
+        if verbose:
+            return u'x-tilt: $\phi = {:d}$°'.format(int(np.round(self.tilt*180/pi)))
+        else:
+            return u'xtilt_phi={:d}°'.format(int(np.round(self.tilt*180/pi)))
 
 
 class YTiltProjector(Projector):
@@ -330,9 +338,12 @@ class YTiltProjector(Projector):
 
     def __init__(self, dim, tilt, dim_uv=None):
 
-        def get_position(p, m, b, size):
-            y, x = np.array(p)[:, 0]+0.5, np.array(p)[:, 1]+0.5
-            return (y-m*x-b)/np.sqrt(m**2+1) + size/2.
+        def get_position(points, center, tilt, size):
+            point_vecs = np.asarray(points)+0.5 - np.asarray(center)  # vectors pointing to points
+            direc_vec = np.array((np.cos(tilt), -np.sin(tilt)))  # vector pointing along projection
+            distances = -np.cross(point_vecs, direc_vec)  # here (special case): divisor is one!
+            # minus because sign is derived of -sin(angle(point_vec, direc_vec)) neg between 0-180°
+            return distances + size/2.  # Shift to center the projection
 
         def get_impact(pos, r, size):
             return [x for x in np.arange(np.floor(pos-r), np.floor(pos+r)+1, dtype=int)
@@ -367,9 +378,7 @@ class YTiltProjector(Projector):
         voxels = list(itertools.product(range(dim_proj), range(dim_perp)))  # z-x-plane
         # Calculate positions along the projected pixel coordinate system:
         center = (dim_proj/2., dim_perp/2.)
-        m = np.where(tilt <= pi, -1/np.tan(tilt+1E-30), 1/np.tan(tilt+1E-30))
-        b = center[0] - m * center[1]
-        positions = get_position(voxels, m, b, dim_u)
+        positions = get_position(voxels, center, tilt, dim_u)
         # Calculate weight-matrix:
         r = 1/np.sqrt(np.pi)  # radius of the voxel circle
         rho = 0.5 / r
@@ -398,12 +407,14 @@ class YTiltProjector(Projector):
         super(YTiltProjector, self).__init__(dim, dim_uv, weight, coeff)
         self._log.debug('Created '+str(self))
 
-    def get_info(self):
+    def get_info(self, verbose=False):
         '''Get specific information about the projector as a string.
 
         Parameters
         ----------
-        None
+        verbose: boolean, optional
+            If this is true, the text looks prettier (maybe using latex). Default is False for the
+            use in file names and such.
 
         Returns
         -------
@@ -411,7 +422,10 @@ class YTiltProjector(Projector):
             Information about the projector as a string, e.g. for the use in plot titles.
 
         '''
-        return 'y-tilt: $\phi = {:3.2f} \pi$'.format(self.tilt/pi)
+        if verbose:
+            return u'y-tilt: $\phi = {:d}$°'.format(int(np.round(self.tilt*180/pi)))
+        else:
+            return u'ytilt_phi={:d}°'.format(int(np.round(self.tilt*180/pi)))
 
 
 class SimpleProjector(Projector):
@@ -436,16 +450,14 @@ class SimpleProjector(Projector):
 
     _log = logging.getLogger(__name__+'.SimpleProjector')
     AXIS_DICT = {'z': (0, 1, 2), 'y': (1, 0, 2), 'x': (2, 1, 0)}  # (0:z, 1:y, 2:x) -> (proj, v, u)
+    # coordinate switch for 'x': u, v --> z, y (not y, z!)!
 
     def __init__(self, dim, axis='z', dim_uv=None):
         self._log.debug('Calling __init__')
         assert axis in {'z', 'y', 'x'}, 'Projection axis has to be x, y or z (given as a string)!'
         self.axis = axis
         proj, v, u = self.AXIS_DICT[axis]
-        if axis == 'x':
-            dim_proj, dim_v, dim_u = dim[proj], dim[u], dim[v]  # coordinate switch for 'x'!
-        else:
-            dim_proj, dim_v, dim_u = dim[proj], dim[v], dim[u]
+        dim_proj, dim_v, dim_u = dim[proj], dim[v], dim[u]
         dim_z, dim_y, dim_x = dim
         size_2d = dim_u * dim_v
         size_3d = np.prod(dim)
@@ -464,13 +476,11 @@ class SimpleProjector(Projector):
         elif axis == 'x':
             self._log.debug('Projection along the x-axis')
             coeff = [[0, 0, 1], [0, 1, 0]]  # Caution, coordinate switch: u, v --> z, y (not y, z!)
-            #  indices = np.array([np.arange(dim_proj) + row*dim_proj
-            #                      for row in range(size_2d)]).reshape(-1)  # this is u, v --> y, z
             indices = np.array([np.arange(dim_x) + (row % dim_z)*dim_x*dim_y + row//dim_z*dim_x
                                 for row in range(size_2d)]).reshape(-1)
         if dim_uv is not None:
             indptr = indptr.tolist()  # convert to use insert() and append()
-            d_v, d_u = int((dim_uv[0]-dim_v)/2), int((dim_uv[1]-dim_u)/2)  # padding in u and v
+            d_v, d_u = (dim_uv[0]-dim_v)//2, (dim_uv[1]-dim_u)//2  # padding in u and v
             indptr[-1:-1] = [indptr[-1]] * d_v*dim_uv[1]  # append empty rows at the end
             for i in np.arange(dim_v, 0, -1):  # all slices in between
                 u, l = i*dim_u, (i-1)*dim_u+1  # upper / lower slice end
@@ -487,12 +497,14 @@ class SimpleProjector(Projector):
         super(SimpleProjector, self).__init__(dim, dim_uv, weight, coeff)
         self._log.debug('Created '+str(self))
 
-    def get_info(self):
+    def get_info(self, verbose=False):
         '''Get specific information about the projector as a string.
 
         Parameters
         ----------
-        None
+        verbose: boolean, optional
+            If this is true, the text looks prettier (maybe using latex). Default is False for the
+            use in file names and such.
 
         Returns
         -------
@@ -500,6 +512,9 @@ class SimpleProjector(Projector):
             Information about the projector as a string, e.g. for the use in plot titles.
 
         '''
-        return 'projected along {}-axis'.format(self.axis)
+        if verbose:
+            return 'projected along {}-axis'.format(self.axis)
+        else:
+            return '{}axis'.format(self.axis)
 
 # TODO: Arbitrary Projections!
diff --git a/pyramid/version.py b/pyramid/version.py
index bbc3afc..6cf8163 100644
--- a/pyramid/version.py
+++ b/pyramid/version.py
@@ -1,3 +1,3 @@
 # THIS FILE IS GENERATED FROM THE PYRAMID SETUP.PY
 version = "0.1.0-dev"
-hg_revision = "4fdb98485ce0+"
+hg_revision = "4b25550ed656+"
diff --git a/scripts/gui/mag_slicer.py b/scripts/gui/mag_slicer.py
index 1ded00a..cd3af7a 100644
--- a/scripts/gui/mag_slicer.py
+++ b/scripts/gui/mag_slicer.py
@@ -8,12 +8,14 @@
 # WARNING! All changes made in this file will be lost!
 
 
+import os
 import sys
 
 from PyQt4 import QtCore, QtGui
 
 from matplotlibwidget import MatplotlibWidget
 
+import pyramid
 from pyramid.magdata import MagData
 from pyramid.projector import SimpleProjector
 from pyramid.phasemapper import PhaseMapperRDFC
@@ -228,7 +230,8 @@ class UI_MagSlicerMain(QtGui.QWidget):
             self.mplWidgetMag.draw()
 
     def load(self):
-        mag_file = QtGui.QFileDialog.getOpenFileName(self, 'Open Data File', '',
+        directory = os.path.join(pyramid.DIR_FILES, 'magdata')
+        mag_file = QtGui.QFileDialog.getOpenFileName(self, 'Open Data File', directory,
                                                      'NetCDF files (*.nc)')
         self.mag_data = MagData.load_from_netcdf4(mag_file)
         self.mag_data_loaded = True
diff --git a/tests/test_costfunction/phase_map_ref.nc b/tests/test_costfunction/phase_map_ref.nc
index 744cc72c6c2e8f3d3b59862b385df75ac9c40244..1990d52440d21a5429123f2bd375e30ad2c771d7 100644
GIT binary patch
delta 846
zcmZ2sILB;)jEamj0~i=UD24+K&0GCFT!NT*wlFDy#h6(qI;>&dxxjJadk<-5h8RhR
z%1uxWqB0CDKqgRCfz;$2#z`!1w2lc+W@1WVgo+qU-pC{)WFP@iKMSgjL4C3!n>LHY
z+G9&L^DzrBGVw`GR$y5q-WBZH<?rm_Eg%3=D8>d+?mStMS$y&b7BQC6Q2noycQG65
z^Dr<nFbHrkfG8l~U|?kjF`2bM{6-ETFo&^C3M|VYH(8yf9Bdj$+5o5x1Xw|Y=;Vh?
zk}TPrYmZJo#-gMMu{f0-B*nns0ij^FJotWA255T<(-M$t>*_gy(#Qsa&E<mWfzT72
zUo?Y!#3l~nGqNy4rCD+li?g}FPGDeQlZ8lvf)WI{lJoP@GE-9Xl2aq0VQ~30Ll;C6
zPEIypE17J-5y8kZ*^xtB5^5GOEFgf12|}}hWEt7PYG86O8m5MGav_I2qr~Qk9Q^#8
zAm{S*Ffg!8)@Jo}fjJmP?}9Qw9tQ!KA7Ff#gJFF0yIJdikv(ZL7pp7_%v4U8Y0Q%w
nS;bXgW<X42V1Stc<Fj}fY@BQ$QBlu`7cek1*wYk?<FEh#(v@+*

delta 282
zcmbPZw!(0NjLHfL1~4#yPz+x#J?i)Oa0z1K*}|j*7Gq|f=&*))!g{WW?>(d$7`Rv=
zDkniTh{`aq0GU8l1yYl97$>p(?mM_;G80n@BUHp-@<t{Zp*c`(RZwjV>XSFJYqLDa
z|7gFNk6D0`$v|SV0?Q&6_N&*6CyTKuO@6^5$5Jt$_5b7;Nm-U01_q|dwUWNNj8N?`
zdKWXyJP5tVy-3~P*~6O$rh^lvl5ujsgg8r`O4XCihMZDNlMOf`IAMBMY*yst=b!Ar
KIbkxNR0RO?usNIn

diff --git a/tests/test_dataset.py b/tests/test_dataset.py
index 4f06f82..2956acf 100644
--- a/tests/test_dataset.py
+++ b/tests/test_dataset.py
@@ -58,17 +58,17 @@ class TestCaseDataSet(unittest.TestCase):
         assert self.data.Se_inv.diagonal().sum() == self.data.m, \
             'Unexpected behaviour in set_Se_inv_block_diag()!'
 
-    def test_set_Se_inv_diag_with_masks(self):
+    def test_set_Se_inv_diag_with_conf(self):
         self.data.append(self.phase_map, self.projector)
         self.data.append(self.phase_map, self.projector)
-        mask_2d = self.mask[1, ...]
-        self.data.set_Se_inv_diag_with_masks([mask_2d, mask_2d])
+        confidence = self.mask[1, ...]
+        self.data.set_Se_inv_diag_with_conf([confidence, confidence])
         assert self.data.Se_inv.shape == (self.data.m, self.data.m), \
             'Unexpected behaviour in set_Se_inv_diag_with_masks()!'
-        assert self.data.Se_inv.diagonal().sum() == 2*mask_2d.sum(), \
+        assert self.data.Se_inv.diagonal().sum() == 2*confidence.sum(), \
             'Unexpected behaviour in set_Se_inv_diag_with_masks()!'
 
-    def test_create_3d_mask(self):
+    def test_set_3d_mask(self):
         self.assertRaises(NotImplementedError, self.data.create_3d_mask, None)
 
 
diff --git a/tests/test_forwardmodel/phase_map_ref.nc b/tests/test_forwardmodel/phase_map_ref.nc
index 744cc72c6c2e8f3d3b59862b385df75ac9c40244..1990d52440d21a5429123f2bd375e30ad2c771d7 100644
GIT binary patch
delta 846
zcmZ2sILB;)jEamj0~i=UD24+K&0GCFT!NT*wlFDy#h6(qI;>&dxxjJadk<-5h8RhR
z%1uxWqB0CDKqgRCfz;$2#z`!1w2lc+W@1WVgo+qU-pC{)WFP@iKMSgjL4C3!n>LHY
z+G9&L^DzrBGVw`GR$y5q-WBZH<?rm_Eg%3=D8>d+?mStMS$y&b7BQC6Q2noycQG65
z^Dr<nFbHrkfG8l~U|?kjF`2bM{6-ETFo&^C3M|VYH(8yf9Bdj$+5o5x1Xw|Y=;Vh?
zk}TPrYmZJo#-gMMu{f0-B*nns0ij^FJotWA255T<(-M$t>*_gy(#Qsa&E<mWfzT72
zUo?Y!#3l~nGqNy4rCD+li?g}FPGDeQlZ8lvf)WI{lJoP@GE-9Xl2aq0VQ~30Ll;C6
zPEIypE17J-5y8kZ*^xtB5^5GOEFgf12|}}hWEt7PYG86O8m5MGav_I2qr~Qk9Q^#8
zAm{S*Ffg!8)@Jo}fjJmP?}9Qw9tQ!KA7Ff#gJFF0yIJdikv(ZL7pp7_%v4U8Y0Q%w
nS;bXgW<X42V1Stc<Fj}fY@BQ$QBlu`7cek1*wYk?<FEh#(v@+*

delta 282
zcmbPZw!(0NjLHfL1~4#yPz+x#J?i)Oa0z1K*}|j*7Gq|f=&*))!g{WW?>(d$7`Rv=
zDkniTh{`aq0GU8l1yYl97$>p(?mM_;G80n@BUHp-@<t{Zp*c`(RZwjV>XSFJYqLDa
z|7gFNk6D0`$v|SV0?Q&6_N&*6CyTKuO@6^5$5Jt$_5b7;Nm-U01_q|dwUWNNj8N?`
zdKWXyJP5tVy-3~P*~6O$rh^lvl5ujsgg8r`O4XCihMZDNlMOf`IAMBMY*yst=b!Ar
KIbkxNR0RO?usNIn

diff --git a/tests/test_magdata.py b/tests/test_magdata.py
index a178de3..8b189d2 100644
--- a/tests/test_magdata.py
+++ b/tests/test_magdata.py
@@ -76,15 +76,39 @@ class TestCaseMagData(unittest.TestCase):
         assert_allclose(self.mag_data.magnitude, reference,
                         err_msg='Unexpected behavior in set_vector()!')
 
+    def test_flip(self):
+        mag_data = MagData.load_from_netcdf4(os.path.join(self.path, 'mag_data_orig.nc'))
+        mag_data_flipx = MagData.load_from_netcdf4(os.path.join(self.path, 'mag_data_flipx.nc'))
+        mag_data_flipy = MagData.load_from_netcdf4(os.path.join(self.path, 'mag_data_flipy.nc'))
+        mag_data_flipz = MagData.load_from_netcdf4(os.path.join(self.path, 'mag_data_flipz.nc'))
+        assert_allclose(mag_data.flip('x').magnitude, mag_data_flipx.magnitude,
+                        err_msg='Unexpected behavior in flip()! (x)')
+        assert_allclose(mag_data.flip('y').magnitude, mag_data_flipy.magnitude,
+                        err_msg='Unexpected behavior in flip()! (y)')
+        assert_allclose(mag_data.flip('z').magnitude, mag_data_flipz.magnitude,
+                        err_msg='Unexpected behavior in flip()! (z)')
+
+    def test_rot(self):
+        mag_data = MagData.load_from_netcdf4(os.path.join(self.path, 'mag_data_orig.nc'))
+        mag_data_rotx = MagData.load_from_netcdf4(os.path.join(self.path, 'mag_data_rotx.nc'))
+        mag_data_roty = MagData.load_from_netcdf4(os.path.join(self.path, 'mag_data_roty.nc'))
+        mag_data_rotz = MagData.load_from_netcdf4(os.path.join(self.path, 'mag_data_rotz.nc'))
+        assert_allclose(mag_data.rot('x').magnitude, mag_data_rotx.magnitude,
+                        err_msg='Unexpected behavior in rot()! (x)')
+        assert_allclose(mag_data.rot('y').magnitude, mag_data_roty.magnitude,
+                        err_msg='Unexpected behavior in rot()! (y)')
+        assert_allclose(mag_data.rot('z').magnitude, mag_data_rotz.magnitude,
+                        err_msg='Unexpected behavior in rot()! (z)')
+
     def test_load_from_llg(self):
-        mag_data = MagData.load_from_llg(os.path.join(self.path, 'ref_mag_data.txt'))
+        mag_data = MagData.load_from_llg(os.path.join(self.path, 'mag_data_ref_load.txt'))
         assert_allclose(mag_data.magnitude, self.mag_data.magnitude,
                         err_msg='Unexpected behavior in load_from_llg()!')
         assert_allclose(mag_data.a, self.mag_data.a,
                         err_msg='Unexpected behavior in load_from_llg()!')
 
     def test_load_from_netcdf4(self):
-        mag_data = MagData.load_from_netcdf4(os.path.join(self.path, 'ref_mag_data.nc'))
+        mag_data = MagData.load_from_netcdf4(os.path.join(self.path, 'mag_data_ref_load.nc'))
         assert_allclose(mag_data.magnitude, self.mag_data.magnitude,
                         err_msg='Unexpected behavior in load_from_netcdf4()!')
         assert_allclose(mag_data.a, self.mag_data.a,
diff --git a/tests/test_magdata/mag_data_flipx.nc b/tests/test_magdata/mag_data_flipx.nc
new file mode 100644
index 0000000000000000000000000000000000000000..a52466f5af96b73a67d866a6ce1c6e3edbca8a64
GIT binary patch
literal 9436
zcmeHM%}-N75TEy6TS_fb6oX$04?|+2#*pw~qEW!Md}yVSa?u1yu?^HfF;a{W5;#fV
zWHd1*8a?<Y=!J{K1M#NuqQ;NJgBN4s$I**A^LD4LEvS9^Fd@6}n76aDvopWnb~hd7
zyFwimzU@BU>jiG#WqVyOG!1e<dw-=a+7;^6{rB}<md<d?A)ZnweL84lvSlZ@P#78%
zT5#Wgi0ll|p&EZ64pn9=h?v@-X#+HUHvn`E8`)uFDSGv7fdIX2L@R1RApkf+fc`}P
zLo57>GEKuJgga@ED~o1Sljo|sS}R}x6KcFrGlPR}D;L8j7IJM9_E+UR&d^vr!@Xo`
zfCtk;FoKcxSPV9!iUQa133Z<c_r$uRJp-{|dn640)0CxwDOeMF+E0Wn`!;FcH=G(b
z@i%U!lbK+s!%U<`lj-r)SlS$%$e8J|Om666{6Z=|IFj6Na%)5{p5@wi0usoBgdt=3
zdq}*S&>6DJWLoH=#sN?S`X?n_!?Z3op}j6SnG#BX-Td+gGJ5n;EE?=OE?h0scEuJH
zg14JzEA=%OZ$9_b0D`U)d)SVpCY#8Gv&1#S)8?0p#k6@u5V*fYE1fngIh^bmZC<&h
zex@z3T?A~4wxOYKZ*sIv%j8#~P4WcpNuq60ZlcTD%~9uRYk_O~=DfwUl}Wl-mQLGF
z>A^ADmL#e_)3)$8v^~Bze=bK`qd4fHQ)VI$cmA|Vp1^&HXge=A(ab^pqw};KfceCi
z2U*%e6rt|Yv}{Z=Wd77;X_R_CM2p77J!7tL&-f^PV~;q2K2PJ}N`1|>L%4iVE+o^v
z`kg9{vMCAff{X}S_@#7t<hg#EF!$8ZTumO50IsuRqvt<Ke;#W?YAzlvAB_*CQ<;fG
za<@e1@ZGW-7Io7!#ehn@1M$wtU4W79SRc@7PIn`%MLE#z&puhwLF|%%`0zcyPwM<W
zEuP;epU)ysPIU&O5z<?WNgL`)+<^1q(}VSV?DJM&J==4PtmpHFw*>3i4{s6Hvmf44
ztY<&mXUQK72}d@5_D}dwV|(^5wD`9&`=(wL0tx|zfI>hapb$_9C<GJ&3IT<HLO>y~
z?hv3#L}3NnFSGQIP;H(n6b)3N=nZ#-d&51!@PJjJXu*;IRVWhiOnf|<$(1Uym3Z(5
z@awjy1eh!S|AK2G4C}(3gI}%M$eOKe)jn|f@Y|6sYr8U6R3>iqeOTL-xn?sqcjgtQ
zJ}aKtC44P-oE&4{rOLt>sn0%~7-Qe1N+)9ZmCsV+Q}(%i^G~oqchqKEiytvktS4J;
ycIWZ@Ql;JX?9MASKDM{Jp51x8u9E*30lWO!oo6>&yX)DVx8CElo1dDOkH8NWW!7^5

literal 0
HcmV?d00001

diff --git a/tests/test_magdata/mag_data_flipy.nc b/tests/test_magdata/mag_data_flipy.nc
new file mode 100644
index 0000000000000000000000000000000000000000..20f3e9a2018345ea673b14f0d31b4971d385343b
GIT binary patch
literal 9436
zcmeHMOHWfl6h8Ofwv<|=C<H|kE{23ejUnM-f>FS>JhaeA*=T~K*am87OQaYfB(Rde
z%4lLtG`jF7=!T8N1#zcwqsB+#!i_QUv2-Jzb7xLl%0qjxEXW<WoI7XcJm&kRGwETW
z+t*p*s&c7LCvaPp?L(c=G{^zv%gx$gx36DyKT!`GI?XPJ*d$lFR8VM2MK#z^XbR-c
z;J*7L&C@`Ia{PfXl<93Ca%zXBJ<znO0jLT#num?0=+(7*JoL5~XHoMC0l*mo3`Pc@
z8R?f4DGG)V?&N%oESgqMp37=$jD!JnsB}W*EDqX@wP-F8$aYZ7za{N>h1%vTJW5kF
zaNx2K3~!(#6oUPzqQq5vd_BGXzEDrFZz$yL2>8K$kyfeU5)6aBj$Xepzg^BB7>!Nn
z_#4&}(UjNMsYhb*XksFkOz6XtDLs))WrnVWug1c|W6|R}w?_2hSgH9eAb~7M7&4ZB
zhs3!LoguqSrhzVM3IK{g|GcDYl;UC&+B=eyX`uwD=a+ww!86Z8L2vgt;cA7pD>knX
zyxklJsIS;~^Vz3|5OkH;!*(_{)kH1|OI$O&YJRu0o;HU_0{53_`O{`B4vh;&n^SJ7
zwX}JvM8c+M8yWfiF+<yoT>K`qNuIzyMYJu+O>{%K7cV?*EpYqTymLKmMUpO-`O{V{
zJrs<#Wr^xq+7`D%+l$8wS2DCUih~}y<Vxh>&Yw2P6WFg4ZR2ti&9<uF3QyY!Scv?5
znx@T18LBo#N#`U}=1*OkMycmRbltePXY>v385b>IJ0eb?A2WEkQeUwh5-#5s3(0hE
zey56~Y+8c5C^Lc<e#u{+exp7h%pH|vtH?tVz;-zqAO9}>IgAad*m$%!9v(@=Qj?Kr
zy+r5KqoTV8bu(n5K`GvWcxPlTz(7xE0O&NQyOCni8tC?CpDgJhwn;!-_@3P-b#|YE
zXZOkH>%hwkUEW}T^hPi_hq@9s;C1oo!FoRSc`LA<=W~v%=ktcQ1nb!kZxPnBAKp@|
zXFuF$$sY^}M>>D@Pxw&d`Rre4@o#DR&3dsAun@2iun@2iun@2iun@2iun@2iun@2i
z*mVd{B_g+i?Ut)_k5O%&DijS=q3HK_`uqKTUjL9$p=iO9097a=;Z%4cn#z<a(v^5{
zdhqMEs08R6{{Mn&A`I)|!`5F$ZDhwH8?_G%@BcJbv9po6qB41Z;Oovt=9*1%{?dC!
zK7Tpa29W!(ms~ydxtSef`+TL@cxLVL&5!4sjc3-5<681RAuxx2OgwYTXqNM^pIkll
zw>!r6`N~4%vX#$#^W*tj`7Fd3$F*c;Z*B_<bYrHOJqOL&naPoC%*Hcox7+hHyT09R
F_XkRM)^h*=

literal 0
HcmV?d00001

diff --git a/tests/test_magdata/mag_data_flipz.nc b/tests/test_magdata/mag_data_flipz.nc
new file mode 100644
index 0000000000000000000000000000000000000000..095ed28c2572d94b2895ef84b8a098d6eddc483e
GIT binary patch
literal 9436
zcmeHM%}Z2K6hH61nQ@%P9Lumw@EL+o5rSq=l-kVrQKLhSn*x)u8FWHN$uUv{T_w7T
zf(VLQ^e5EDP0~Wz6>Tc|fEH~c=wsO?JLlbdW_0|hc{&DhuRhPa=iYlhe!n~SGRN7r
zKueLQ+@rhQz->vk_tipED+jdCH>$#IfiB(qSU+It45u98CE3!WgGN*KRDc78p+WWx
z?t4$rJOgwn#UF@5iP;DurY5N00rg7;fUaSqdDvKrUUietM{hfE7B#;R030DePps#u
z6@F2HrlAXAPR>Wqq8X)RxumMn3K+nIGB=b>;h@vX#qbD6j(uYOO}UOUG}h1X5KT3}
zg=rxe{!nuy0=rQ~f$R7L+B<@sk@j$BZ^Yjm3WE0nWocjv)`HIFj-WNaNzU&cNQ|2J
z8#R;hlt0j7#u7vE<Y;0zY4(k!%;az?Gjuh2B@yi#j2|(%)u9#FV&x|R2`qzzA!GSx
zNZh;77_!S`TIixC51<J2&q=xl$S*dby)8MJ5K4d=e)$U-KK(2b_P3oCrWR?tV)F~Z
z+s(C?`kI3`pL3!YLDz{rY-bYV^<*Ny#2tl~N8iq`rp+aS!2Km!?zCCS;o`i}=9XJ(
zC2hWP5wI=V`ul%<$j~+^lV686$rCuoiMDyUiLPsRhw@Kb1Kc`1<6cc$fuxIN?zB}%
z3wfh$L87{nwz<vF_WaT8<qU0g;-H6CnTb5y`O_wO0_QcNZA5OOspI<B{L^*}W@A5|
zq-hIKgt|l1(lN=9`BRssQR?{+T{SN58FP($#zW~F2gM2WeG(5>>T8bu!sMGmA(`&=
z?^JP=O-OL(WJJ)wFS*N8ul4(cxvPxUYVwc-a9kQ58u=#exvUMTIe4^iDB7P)q{d?L
z8i~%yhXr>m>LzK40mXO+;+>JX07LDOZlKeg?nd%OIneFTHd)d^?2v$X@O^oo)XV$i
ze0iU2z6!lK-|7#CNN+ilbEqqE1I~+257zUs&s%}@JfCA^J)bwcC0NgPc#E)}?eLaj
zJ=@_vOa5R;IMVU6eZq$t&u9BWi+_vLZ|X%Mpb$_9C<GJ&3IT<HLO>y)5Kssx1QY`Q
z9RgH|$gW^}WtQGSs?Ae{qLwNYUBQ-MSFqC`?6oQs4OkMO3PmiMijKxpnNmf%5)W=4
ze%%(80CUa%UvN!?VV!$${HIkL**4Es?E~EhKMwBMUe8=n8N1j0WqUny&1QJ!;yXrO
z$*n?Qx)C7zVcXey>T|O@#_MyHcKz91muq}H->yHq>)5Z7{}ute{MlV+H(R^@?5_LY
v<AiC<P(53Q!Sn)}%6b^zY(4ciJI3pCmHCKeBb&L#$MZL`nU68{o16R%!h_ax

literal 0
HcmV?d00001

diff --git a/tests/test_magdata/mag_data_orig.nc b/tests/test_magdata/mag_data_orig.nc
new file mode 100644
index 0000000000000000000000000000000000000000..b71ff4c838615b8dd5d43464e74ce0e1a9ec5f92
GIT binary patch
literal 9436
zcmeHM%}-N75TEy6TS_fb6oMiN4?{wt#*pw~f>FTs@u8)Sl#3=vify2VwnT~%LV_m=
zoQx*MM571)1if(4cp%<1Uex%Jc<^FO{5X0Mci!%_g_dG5uO?&{%DkPOot^pp_U)#_
zQopaa!d2%|olc;(CfbK4u4xei%9k6B!G7P6>VB*qG<BL?4AGR0(xrleCN=e7V?tA4
zqXqTd$I(s$6{^_}grQ3B0v=O0wC#enH4Q*jSVKG3Nb+7&x5tA|yO|X>FBbq5A;3sv
z<e3?MMVX?oCAd3b&sOG5t47aNjSXhN06Nq<p>}}{+Ra=v7hlM>pWELQ^C&}Y^9&E8
zsRj;~76ZfU?+JxqFH>RQD*N~b28}>yAQ%`8d3*c@xX)vj8cV^P5a<~+O#5zOKRgke
z*4b}ZPefB*U#}jC#iNPoSTdoH&7|~1GL;#+622S@k4;98>eO197sqPDXATKuK|+v`
z+!_++9_Ea(OJthpGK~eGaP-d#x+ZX4*0}bz;AD;~0h;OIFJ$o4^H9*+e}=nS#qG+P
zmkZi%j(ylyY_$38bE6D&71?9$bZoW_U06$8JG^Rtw~|Yng9pL(7jMPWW+ulbTSl8x
zY^n9MdFpt;+oElJ{P)KUZSx}eO=uH5fqfQfTM?V+x^g#eJ#8Iu>&T)rm$ouN7s=vj
zs}~+DqwT3cbv<p%|3ce~M@yG7w6*es9{NNk;&7+8P4EQvt4Q0F*hCAR>No3YI|fUU
zpHI@X`7lD&rYPx{M9B2krD+s;Iz)5E`8}iOxo2FMzIKS8KtJZ$;fj65c7VHlTh1lX
zz4@KWkFq%d?y`soI_RNzdGd{VA2D~-;#@@>k^r`g$@tWF;m=`iNX15@<?--%B9@wo
zM4JUVCmxpFF{zu!DH>F=JCNNOnG4WA5E=$N&GBx;wJ-;~{mCauJcw-q5EpyS?xQ-p
zkBevb(dTRb%X59+pda<-V!{Ty0ym(%==2~x9s9HuNKf_@BkAe9p)Emr@<UsM^yG)O
z6zRzi^-0o;A>c^IPyP`fY-CUVxt9H{Oh3s7iGV~vA|Mfv2uK7Z0ulj<fJ8tdAQ6xV
z>^KCl60xy@?G{<OC$Tn<6^a(DPz)Kp#*h*48lz@~qJxzLutE_Dr^3_GRHjssuEc}W
z!@h3wN`RjC{})sfAy}6mbpA4HBPCnetbMTMjZc#`rCpgSDl_+nzm|4os@WtLFT4l1
z)|LAqKCBoc-^EHRVkzXa*!al4kWVYdXk97)F9KF@Tgc~jvn}M)im{y@=XUdx^YSBL
i#U3o=Gk+9YYYX|bVr=V(@>^IDOCg{6qmbItBJc+*j%{-Q

literal 0
HcmV?d00001

diff --git a/tests/test_magdata/mag_data_ref_load.nc b/tests/test_magdata/mag_data_ref_load.nc
new file mode 100644
index 0000000000000000000000000000000000000000..200f66fa5f6c43f56e3746ccbc70013b6f206164
GIT binary patch
literal 7684
zcmeHMO-vI}5T5;MDYZyZ2#Q2jV?rW^kkD|z7-+W3pQVj07fpaHwvi^ZB~pwuBzRFH
zM-IkB6Azv|nt1VKOvHHe=EaK#Zzk%&izj@u?@e1CG{qn4Pj+BCJM-qv%=>2dy)xVx
zi;Oh+JN&B82Si(f^y6I9CkD>Enls~Lk%Ss}s-ARonnw)L6q)-~P|&2U6LiKj1!N17
z1831r0~K1?57^LR3<1w+7(xdiw50*43L~^*M4U^fhJ!)8JIJh%OwIr(LxAbj^b05b
zrUpe}A-FqX&mwcuTG4aMi7qE$00Y{6(7wh7J<eJ*KM$lI<@Ot59<5NTzQP_f)xgV^
z#lSG5;bamHGZiMTvMVw^X~mM`@z`wA3`Z>pT*6gqYzfYUSa{NM?1zQ@)O=>iV86DJ
zOBc+@h>^-<)48QgK4;7=7mQrKP#(HwU(MKa3+Y~isGoW9Zgzd(kU#|_SScjpwIT5x
zLVHc2VmhT@ntcF;qkln^YaZh=;@Z2SCPl6U=%$-J@W;lJK-?TV&s}Yb#hWyfKW^S5
zC@VTWe4gSg16{@HF`CG%gwVzQ7A^!<apLtqSvH?|8@HD&*umlZsj?+{US`W>TlI2R
zRmvvn1fCTv+cWVH-BRv5F5I&1*n;@W@LZs}y=?2hQns1i#;@hF_4AD$MtSvUbEmgW
z)CoM-v22UtAzB+$Kh^zhi(rPTt|*ifZIUvtInK`+W0!Nrk0+3JntS}R$~IS&75$h%
zN4TcVwfdRLt5+1@t`oQebo1kUBYd+3G55CPT!j`5`sI9f@w4#f?c{?r+L*Pk<T8cj
zRJyx?o1A;paL*~-D#{u(vonyL$mIhtI-Z;Y+|BVM!C1HkJpIWhaomXYM)twT&#p=t
z)s->^uawc}+vuB%qh>scdMB8$K~dlav@Y5`NKe~7m74TqPdSpF_8a<2LVEHe-m)EW
z1!yHb`Jws5>BSIml<dhr;)6u?lqc7+&*svdd*dSDBH$w6BH$w6BH$w6BH$w6BH$w6
zBH$wM-ywjV2)TnD5UUI<U~e8f6n)sCNLV9Q!it&Jtka<wU@ZacP^9dFy_7DLTNR~F
zJotj_&u!ibFn0a_1@%M-*7b*j-<;k^-6A`^4;J2fztC3S$kb6;elYd1zLBYClfOOl
zj*$Po{KgOfUPQ7?`f6OZm*r~n{uaHQuPoQfQ?{4oTJ!((xSX#n*UC?}m*raX_dfm|
DR;BZ!

literal 0
HcmV?d00001

diff --git a/tests/test_magdata/mag_data_ref_load.txt b/tests/test_magdata/mag_data_ref_load.txt
new file mode 100644
index 0000000..293c185
--- /dev/null
+++ b/tests/test_magdata/mag_data_ref_load.txt
@@ -0,0 +1,66 @@
+LLGFileCreator: test_magdata/ref_mag_data
+    4    4    4
+5.000000e-07   5.000000e-07   5.000000e-07   0.000000e+00   0.000000e+00   0.000000e+00
+1.500000e-06   5.000000e-07   5.000000e-07   0.000000e+00   0.000000e+00   0.000000e+00
+2.500000e-06   5.000000e-07   5.000000e-07   0.000000e+00   0.000000e+00   0.000000e+00
+3.500000e-06   5.000000e-07   5.000000e-07   0.000000e+00   0.000000e+00   0.000000e+00
+5.000000e-07   1.500000e-06   5.000000e-07   0.000000e+00   0.000000e+00   0.000000e+00
+1.500000e-06   1.500000e-06   5.000000e-07   0.000000e+00   0.000000e+00   0.000000e+00
+2.500000e-06   1.500000e-06   5.000000e-07   0.000000e+00   0.000000e+00   0.000000e+00
+3.500000e-06   1.500000e-06   5.000000e-07   0.000000e+00   0.000000e+00   0.000000e+00
+5.000000e-07   2.500000e-06   5.000000e-07   0.000000e+00   0.000000e+00   0.000000e+00
+1.500000e-06   2.500000e-06   5.000000e-07   0.000000e+00   0.000000e+00   0.000000e+00
+2.500000e-06   2.500000e-06   5.000000e-07   0.000000e+00   0.000000e+00   0.000000e+00
+3.500000e-06   2.500000e-06   5.000000e-07   0.000000e+00   0.000000e+00   0.000000e+00
+5.000000e-07   3.500000e-06   5.000000e-07   0.000000e+00   0.000000e+00   0.000000e+00
+1.500000e-06   3.500000e-06   5.000000e-07   0.000000e+00   0.000000e+00   0.000000e+00
+2.500000e-06   3.500000e-06   5.000000e-07   0.000000e+00   0.000000e+00   0.000000e+00
+3.500000e-06   3.500000e-06   5.000000e-07   0.000000e+00   0.000000e+00   0.000000e+00
+5.000000e-07   5.000000e-07   1.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+1.500000e-06   5.000000e-07   1.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+2.500000e-06   5.000000e-07   1.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+3.500000e-06   5.000000e-07   1.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+5.000000e-07   1.500000e-06   1.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+1.500000e-06   1.500000e-06   1.500000e-06   1.000000e+00   1.000000e+00   1.000000e+00
+2.500000e-06   1.500000e-06   1.500000e-06   1.000000e+00   1.000000e+00   1.000000e+00
+3.500000e-06   1.500000e-06   1.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+5.000000e-07   2.500000e-06   1.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+1.500000e-06   2.500000e-06   1.500000e-06   1.000000e+00   1.000000e+00   1.000000e+00
+2.500000e-06   2.500000e-06   1.500000e-06   1.000000e+00   1.000000e+00   1.000000e+00
+3.500000e-06   2.500000e-06   1.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+5.000000e-07   3.500000e-06   1.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+1.500000e-06   3.500000e-06   1.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+2.500000e-06   3.500000e-06   1.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+3.500000e-06   3.500000e-06   1.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+5.000000e-07   5.000000e-07   2.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+1.500000e-06   5.000000e-07   2.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+2.500000e-06   5.000000e-07   2.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+3.500000e-06   5.000000e-07   2.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+5.000000e-07   1.500000e-06   2.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+1.500000e-06   1.500000e-06   2.500000e-06   1.000000e+00   1.000000e+00   1.000000e+00
+2.500000e-06   1.500000e-06   2.500000e-06   1.000000e+00   1.000000e+00   1.000000e+00
+3.500000e-06   1.500000e-06   2.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+5.000000e-07   2.500000e-06   2.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+1.500000e-06   2.500000e-06   2.500000e-06   1.000000e+00   1.000000e+00   1.000000e+00
+2.500000e-06   2.500000e-06   2.500000e-06   1.000000e+00   1.000000e+00   1.000000e+00
+3.500000e-06   2.500000e-06   2.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+5.000000e-07   3.500000e-06   2.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+1.500000e-06   3.500000e-06   2.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+2.500000e-06   3.500000e-06   2.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+3.500000e-06   3.500000e-06   2.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+5.000000e-07   5.000000e-07   3.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+1.500000e-06   5.000000e-07   3.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+2.500000e-06   5.000000e-07   3.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+3.500000e-06   5.000000e-07   3.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+5.000000e-07   1.500000e-06   3.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+1.500000e-06   1.500000e-06   3.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+2.500000e-06   1.500000e-06   3.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+3.500000e-06   1.500000e-06   3.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+5.000000e-07   2.500000e-06   3.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+1.500000e-06   2.500000e-06   3.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+2.500000e-06   2.500000e-06   3.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+3.500000e-06   2.500000e-06   3.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+5.000000e-07   3.500000e-06   3.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+1.500000e-06   3.500000e-06   3.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+2.500000e-06   3.500000e-06   3.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
+3.500000e-06   3.500000e-06   3.500000e-06   0.000000e+00   0.000000e+00   0.000000e+00
\ No newline at end of file
diff --git a/tests/test_magdata/mag_data_rotx.nc b/tests/test_magdata/mag_data_rotx.nc
new file mode 100644
index 0000000000000000000000000000000000000000..665048b25056a7fec75a8e80ca9abaed58dcfd4e
GIT binary patch
literal 9436
zcmeHM%}*0i5TD&`OQ}VQLQo`OH6$cz3<)157z1QmKD3lbd(i|*v5nNwmPj!|NZ=#^
zFGdq%qKOCp1if%j;(@Eiiy=y)2QP-ikE34j&AvBnDRgDml!NTZI{W6$n>X{DxAVfV
z*yr!5@YH!^w;PBy1+6!7Nwb)czTP`N*ykUTy)Wbwx=eA3DOys<Ju*nBQd18O#uNz(
zHE8TThk6RgP|f~96sput;68Oh>j7xpQ~+d&5$Z7_&PR=1J|DgvWLiiXX8`0Oz;JB%
zweEgJnIy3-xH+NEcIKp1qvfjO4Z6bsD%856c8*Ot^;i@S-^g*4>)#jaC_<%pgr`td
z0T&C4fuRMv!(lkgMCiE89)JHpAQbK&42^`f?qC4CS1?M2g`h78bq@q|{Vt&&8B0v6
z>?f+G;u+1~qs9`+cxp0{PN}0)88wy8<fd*#ZziIn<MA^pQ46!;T5b5kA%Q$dTx<r3
z_}?LMA3}XaqGTSyA)^w2!qLAZ(ly5S0WN(gaxx>N&_pl0;Oh`s8`SzPa#O2#Tp4Mc
z(Q$L>kx33Ze9oB>2D*&dV{{=g-HIkko49s(aC*VLJ#BQ-iJMQG{!y?vN|rXa@NH|_
ze04f{U@2`2cRzV@X`2;iwn*AUp1?VcX<HVD=#KO_S^BiK!MnB(%iGgd#)Ajq=F?U$
zER-y5D+1N6X<Pc2w7q%0cq5m#7GCI~S41L;JAG{;PvE?bX`2v-Xs$#4R{FG^g~iy<
zSJ|}r(L>oGN#qk1N&4!tX%wLpo#*$Ay2Cx=!SI!nyaN50WyKZ8lH(XRxmM0OrMvi@
z%1had0C!1v1nu-<o^QPTokq-EwYXLiMH0YqEuEbBF6_DV11ULZwmccVo=Rk<V(}({
z&iQ9$kMz{d;t~Za*&WD=PwoN?_J<>Y)g13e+zVsC+n;O_$3pB7fOyz@{ussiW86G{
zj5gl~-(K$327@TqHxoKI6u1G!Mb(4kRQBm8keu|%N0L*$p`$`_vO`CO<Yb4A6v@dB
zjfvBTA>hdRPxcWXB+@7QT+05h%)Z$#HUc&RHUc&RHUc&RHUc&RHUc&RHUc&RHUj@0
z0@#Tt>|lFEl-_ad&0~k689Nk1fu6unAfyFG^bSQkYYAY7A{Na=C*zr1t0LQp2e*&?
zy3IQQ>W+WEpq>c9y7aW;m);xMvz_(c2irdIdAw$CcczZY)RV}@-tJ62oAkofkA%E=
zKHmvY_>h%CIgW{p=19jZH?nDU&a4llZ*@PTb!6KtH?nDU&a4mnMt`0~YT=n*Vap00
zvQsF>@owfw$1JzHpV2zAK9IhVO{;Tc+bp-bpV2zAKI|KPcEqia)p};M&Wazn#>l4C
PIop@5xLU<yf2{ih;!f9o

literal 0
HcmV?d00001

diff --git a/tests/test_magdata/mag_data_roty.nc b/tests/test_magdata/mag_data_roty.nc
new file mode 100644
index 0000000000000000000000000000000000000000..c4623430902ca3a01b97680416aa688bd3903a2e
GIT binary patch
literal 9436
zcmeHM&1+LZ5TEy6lE#>7>j(7<d`dx35y4tPk+vkMt)?wDy(nl&O>6^gDmE1@1$(mC
zgNT9%3L^diUc7iwJoF}r7ZE?egBKC}IEtX|yxm{Yv`OFAkRJ4{ea@TN*_oZ+?3<<2
zs~yqy+R&zu84MD}Z#CXEO37X|VSKv2t*0Z}Yla`0+Z~zZS5sIrpN32_SY`bd@(E%Y
zG+zUJ_z>$^WYRkEkCIexw~*XZD>bd8rr#EkX^6pkVt`7nv9+#_-&P4N9uY~1utTC#
zsZ-CK?bp^AhHxRxDShEt4r?7-uHUxR*)Wk!4MA$SEGGR<ELKQ5@@<s*H`F>rXqAs}
z7pqzn5Mc>0BJs9Fg4PNVZ`>45w6iPLo#^c8?oUM8;xP)J<R~o>g0rBztt;l}w<`U<
zq0E>qev)=Jor^@<?NnwYogK@JX6=FToShxb6{apE&u5YY!|6RX#y(*sFuV1mgoNfm
zQX^(~fVx8xT*La7fn;uBR*WiuBGEst(lsRgkd)q1IhjyW+JP4h8hCjBa8IP;m^3xZ
z$5o7oBsgw?4a^%p96tX<zX09j?1^zSbE%0<R5x)4=xXZwlZ9yu$W4gzCCAd!<^(4e
zS1oNp^)(i!t!|Ut@L#2EaPZgrLfR%(@MY4b@<jeioVLg65M43uj8s2u&2)43bZ}wX
zP@q7Uo;IhJRWof*6{^K)o2evi!JlWZ71Fj(7J51&55oMfP34LF7ddU`6jGNDnqR7)
zw*B;S|C`78v_*M`rq3|2ClpD1b@?=^P|D8Bd&Vwt&xH5}vUbV}^lefUSLO}hW@+;E
z3Q6&mmcLVFDVtE>&Zr$xGhUWXTkky2GUkB>UTdf#N#r{-I&$u-vKMg5zv08|ijm}C
zHj^7qrFSSa4nL^5?WAs!msqq~+=1d07cRhfXQGd|n)BVrzBmTH{b3W73$afD5)$uo
zc^1#*+4)?aZN7@XIB_J>6KA>OOzAMIa3jQp>H#^*K8^z9(8nGjN4>!jfgE;lgdm3<
z93jYI2RtY~3<XDi|FF;a@PIz-OR4yOb^gtJ@euG3@DT72@DT72@DT72@DT72@DT72
z@DTXt5a3S4{0??lMHwFE-aL0G_Hu`!H`X5Ojde$2{Z5CXS+oSWLy=16l4I#yp;eLZ
z#8a?N{JJeW0d~p1U!W%fSZD4X{NeOQmd&%%`w;G9ABNX2uV-{r#_#rhUS7}W*^Ew~
zehcLPA3D`Sga=WBXNsXKgT5QpwWq7+<`46__H^}N*Ny7h)75kHhk0Flx_Yqd(P9L2
z@zd2SwqBmowWq6B-c#|6u0377V(aBOU3<EE<vkV8=-SiOE4ID}Hznr&qi5==2v<Fb
s8$8pc%AoH?EAdfkt(!m0ms(qi40hdUB|b{6b@PY$Qfn)b!S3Sp54Q@|J^%m!

literal 0
HcmV?d00001

diff --git a/tests/test_magdata/mag_data_rotz.nc b/tests/test_magdata/mag_data_rotz.nc
new file mode 100644
index 0000000000000000000000000000000000000000..a55ee115635f01c1598a0f147704f10c68f1c206
GIT binary patch
literal 9436
zcmeHM&1+LZ5TEy6lExTqsx8%8@F@jBMTCBc6|pr*KQt!V^rBKrG(iJRQfw-=6xx&0
zo<u<e1rK`g5Aag(0}tX+yog^&4_-v@<LF6e-tOe1Z7@%h3T3x_o!xn}Gqb<nW|vH7
z`vN`dJWU?m?FMeYWc$z}G<)R>?en#5k-ory?tQ3lw{(V6zTzp1rAG&iOd6WOfx^&W
zu?3I42guF<9qRE1Vo+xu0+CY}w5^4<Uj~4#VIw<iEXAm$%jcusdbFbE7XpAY1Q?1B
zJ+snZSEFedLb#Lm7+Ex<o;=rW+iE2YU_zrC8mIB4(^`w+5rG_=h5dCok5_1vUg0h>
zHNb_-LNNTH?r0P?qKXpN@d@-F3x=cpk??TT-yI5q_Y|$tz$I7{!rjM$mVKABAH0wp
zGx0ZOrW09zpvR0SQ;GCgGLtsXj%UquCYyhCDRwa#J3ErtV{&UnFRtfXKM6>n01}3b
z<)0yOZ$M|rE|Y1Yi<&BcBG5k}>AFC1u?g)>$;qTp0_@;U88UMCMKt2?J1Sf~r|pW(
zF9dHl*CrZk4&Hpu$zcRtC-$%%Nlvtpi|P{B4zJqZ%`c^`MkE0&?bFsQJyeZ0x7<?2
zwE3Du+`ppj?ZUwQJZ)1h;i?2}k|%IZ5N-2v6J616r>aleKA4UFc(Rl>nK2#<qS>d-
z+LJiBYP3C-xfRnk_b;?PdT=_Cr>#{S^w29YkcT^e+9XflyiBx>%1t!gsei3LZ5?o9
z_f>a}wg6?QJ2WkqlT4XEbvYWPo)6I_@5Mc1E_2U#X!*uYaRPmx!o!uunq!M_`DTrf
zOn2!!RUBoL65KhN5p;0JzWjK6|6{`3)kt$Sc}M~{&Sg@g-=sg6b&hBbe!C_WJD*Nw
z$K#0|5`}~JYHnH7P0<tsYVi)lJ0pJqhWev}K&Ls~jTDR4K({~pWJw3HLjvN#e&LwZ
zg<}d{I3}O3LoZME`XeFITfw9a4JB^C>*CXc^?dB}R$x8bbB?U%^M<zs>)8))9oDlS
z-a4#jKRjm19}EdcE`Rn<_)uee_Aj*fw>H;P9fg2GKp~(IPzWdl6aoqXg@8gpA)pXY
z2&_5;s1mWbg6);7^o~$%o+=c3sX{Rj><JD8!~Wo~RiWs>k^ogG;<0ROERoHZDsq*0
zaQpD<wx|S{%l`j@Ya$Hm+}+NfR&8X(B3rc&3?Ez=X;|6FTu~XnGx%j?BXi9rbM?%7
zT>6Z7W}afV;8`g>$0=79=16^>S8YAVDOXlIpHlwI%}?3o_fAEyKzGz;4i!H_TdX4+
zZk4iUf91+b#kK3F-2B+iuK!Beb9^QL4+53q(XJo6tF`ODQueDgPrK_<_RAsg8~9e%
Am;e9(

literal 0
HcmV?d00001

diff --git a/tests/test_phasemap.py b/tests/test_phasemap.py
index 472222d..411b751 100644
--- a/tests/test_phasemap.py
+++ b/tests/test_phasemap.py
@@ -17,22 +17,59 @@ class TestCasePhaseMap(unittest.TestCase):
         self.path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'test_phasemap')
         phase = np.zeros((4, 4))
         phase[1:-1, 1:-1] = 1
-        self.phase_map = PhaseMap(10.0, phase)
+        mask = phase.astype(dtype=np.bool)
+        confidence = np.ones((4, 4))
+        self.phase_map = PhaseMap(10.0, phase, mask, confidence)
 
     def tearDown(self):
         self.path = None
         self.phase_map = None
 
+    def test_copy(self):
+        phase_map = self.phase_map
+        phase_map_copy = self.phase_map.copy()
+        assert phase_map == self.phase_map, 'Unexpected behaviour in copy()!'
+        assert phase_map_copy != self.phase_map, 'Unexpected behaviour in copy()!'
+
+    def test_scale_down(self):
+        self.phase_map.scale_down()
+        reference = 1/4. * np.ones((2, 2))
+        assert_allclose(self.phase_map.phase, reference,
+                        err_msg='Unexpected behavior in scale_down()!')
+        assert_allclose(self.phase_map.mask, np.zeros((2, 2), dtype=np.bool),
+                        err_msg='Unexpected behavior in scale_down()!')
+        assert_allclose(self.phase_map.confidence, np.ones((2, 2)),
+                        err_msg='Unexpected behavior in scale_down()!')
+        assert_allclose(self.phase_map.a, 20,
+                        err_msg='Unexpected behavior in scale_down()!')
+
+    def test_scale_up(self):
+        self.phase_map.scale_up()
+        reference = np.zeros((8, 8))
+        reference[2:-2, 2:-2] = 1
+        assert_allclose(self.phase_map.phase, reference,
+                        err_msg='Unexpected behavior in scale_up()!')
+        assert_allclose(self.phase_map.mask, reference.astype(dtype=np.bool),
+                        err_msg='Unexpected behavior in scale_up()!')
+        assert_allclose(self.phase_map.confidence, np.ones((8, 8)),
+                        err_msg='Unexpected behavior in scale_up()!')
+        assert_allclose(self.phase_map.a, 5,
+                        err_msg='Unexpected behavior in scale_up()!')
+
     def test_load_from_txt(self):
         phase_map = PhaseMap.load_from_txt(os.path.join(self.path, 'ref_phase_map.txt'))
-        assert_allclose(phase_map.phase, self.phase_map.phase,
+        assert_allclose(self.phase_map.phase, phase_map.phase,
                         err_msg='Unexpected behavior in load_from_txt()!')
         assert_allclose(phase_map.a, self.phase_map.a,
                         err_msg='Unexpected behavior in load_from_txt()!')
 
     def test_load_from_netcdf4(self):
         phase_map = PhaseMap.load_from_netcdf4(os.path.join(self.path, 'ref_phase_map.nc'))
-        assert_allclose(phase_map.phase, self.phase_map.phase,
+        assert_allclose(self.phase_map.phase, phase_map.phase,
+                        err_msg='Unexpected behavior in load_from_netcdf4()!')
+        assert_allclose(self.phase_map.mask, phase_map.mask,
+                        err_msg='Unexpected behavior in load_from_netcdf4()!')
+        assert_allclose(self.phase_map.confidence, phase_map.confidence,
                         err_msg='Unexpected behavior in load_from_netcdf4()!')
         assert_allclose(phase_map.a, self.phase_map.a,
                         err_msg='Unexpected behavior in load_from_netcdf4()!')
diff --git a/tests/test_phasemap/ref_phase_map.nc b/tests/test_phasemap/ref_phase_map.nc
index 4b69f864f077c05a9d58b7f12e3f04182fc8d131..62d32b4b1925c7555606977fbe34240f4a6d8214 100644
GIT binary patch
delta 778
zcmexhFwb;?jLJMI1~4#yPz+h?l;8P#xCAlrY++IYi!rlKbXdc@bAjW;_a4&B3^9@r
zm7Aa%L}eIQfJ~sO0;$P4jFVW-*O=){W@1WVgo+qU-pC{)WFP@iKMSgjL4C3!n>Ndz
zqa5X%`IrS5nfRn8E3hmQzZRVt;P33=Eg%3=D8>d+?mStMS$y&b7BQC6Q2noycQG65
z^Dr<nFbHrkfG8l~U|?kjF`2bM{6-ETFo)3(>OFzU>MZ48(?HS&Ky4tv3L-=&KV*_*
z@l|l~ntY5!NfBakDmzGufx!bp!EAZ({j3bo_7tW#kZbGeIf2s127=Ayg6V<KGG=1O
zKt5s<2k{wMn4!`vxrxQuTwo_KFtEu&Btbz50$j=Yd1;v`sd>q%k<c)>e43#PA_*ra
z8?cp3HsFY0WSQ*9Aub6u3m6s<z{CWh*+8<4>|ix8IT#I7!#TN-L!MD$^F$7Qeol~c
zd3qQaSSD+;`ntdz45N2J86b~?0L%|CKFq-|zWLp(b->7;G?|N4mIY=iC(JbF$&IYy
sDlju3CNeO<%z*JXbFDN5#*^4&HwmrDADCFZ8G)vQFc>u0Gcb^k0i{)FGXMYp

delta 282
zcmbPd`oUm=j7ot70~i=UD26_Rk|2K%mmnsdElf&aF=pn84r`bvtmm5e-b0#!fr}NQ
zauQU7s0;%OkO@>(AT>FMaT1HMMULfUCZ-fdsEEPjjZ88^bD-L)pxPMJCvRleW>KmW
zFxt$=EWpTQATe2iWf6;P(^kgGVr)v2U$DrrRLp1nKRHHHmL-RQfoXEBq^~X`R6C5`
z#SAkKLbq@I^ugcR!<z@DgA=BbadN+eI7^*M)sxMJoKj4a4LBk=VR~0=R^;U8pX|W7
JY_f`!769sCH2nYo

diff --git a/tests/test_phasemapper/phase_map.nc b/tests/test_phasemapper/phase_map.nc
index a2c2ae7e47b80c712b796fb8bba36a84403fa3a0..dbe3234498122e809bda8ee5e062cc54f43c456b 100644
GIT binary patch
delta 775
zcmexhFwb;?jLJMI1~4#yPz+h?l;8P#xCAlrY++IYi!rlKbXdc@bAjW;_a4&B3^9@r
zm7Aa%L}eIQfJ~sO0;$P4jFVW-*O=){W@1WVgo+qU-pC{)WFP@iKMSgjL4C3!n>Ndz
zqa5X%`IrS5nfRn8E3hmQzZRVt;P33=Eg%3=D8>d+?mStMS$y&b7BQC6Q2noycQG65
z^Dr<nFbHrkfG8l~U|?kjF`2bM{6-ETFo)3(>OFzU>MZ48(?HS&Ky4tv3L-=&KV*_*
z@l|l~ntY5!NfBakDmzGufx!bp!EAZ({j3bo_7tW#kZbGeIf2s127=Ayg6V<KGG=1O
zKt5s<2k{wMn4!`vxrxQuTwo_KFtEu&Btbz50$j=Yd1;v`sd>q%k<c)>e43#PA_*ra
z8?cp3HsFY0WSQ*9Aub6u3m6s<z{CWh*+8<4>|ix8IT#I7!#TN-L!MD$^F$7Qeol~c
zd3qQaSSD+;`ntdz45N2J86b~?0L%|CKFq-|zWLp(b->7;G?|N4mIY=iC(JbF$&IYy
hDlju3CNeO<%z*JXbFDO;Y#`wfz=#GI7#i#;!2qN=Wi9{!

delta 282
zcmbPd`oUm=j7ot70~i=UD26_Rk|2K%mmnsdElf&aF=pn84r`bvtmm5e-b0#!fr}NQ
zauQU7s0;%OkO@>(AT>FMaT1HMMULfUCZ-fdsEEPjjZ88^bD-L)pxPMJCvRleW>KmW
zFxt$=EWpTQATe2iWf6;P(^kgGVr)v2U$DrrRLp1nKRHHHmL-RQfoXEBq^~X`R6C5`
z#SAkKLbq@I^ugcR!<z@DgA=BbadN+eI7^*M)sxMJoKj4a4LBk=VR~0=R^;U8pX|Un
JVKSeT0|4p9H0J;S

diff --git a/tests/test_phasemapper/phase_map_fc.nc b/tests/test_phasemapper/phase_map_fc.nc
index 2d614499502b8fa35af42c961d6ce4b971afdb58..ea13e499169e9b8c32baead2c3f5baee40e075ef 100644
GIT binary patch
delta 775
zcmexhFwb;?jLJMI1~4#yPz+h?l;8P#xCAlrY++IYi!rlKbXdc@bAjW;_a4&B3^9@r
zm7Aa%L}eIQfJ~sO0;$P4jFVW-*O=){W@1WVgo+qU-pC{)WFP@iKMSgjL4C3!n>Ndz
zqa5X%`IrS5nfRn8E3hmQzZRVt;P33=Eg%3=D8>d+?mStMS$y&b7BQC6Q2noycQG65
z^Dr<nFbHrkfG8l~U|?kjF`2bM{6-ETFo)3(>OFzU>MZ48(?HS&Ky4tv3L-=&KV*_*
z@l|l~ntY5!NfBakDmzGufx!bp!EAZ({j3bo_7tW#kZbGeIf2s127=Ayg6V<KGG=1O
zKt5s<2k{wMn4!`vxrxQuTwo_KFtEu&Btbz50$j=Yd1;v`sd>q%k<c)>e43#PA_*ra
z8?cp3HsFY0WSQ*9Aub6u3m6s<z{CWh*+8<4>|ix8IT#I7!#TN-L!MD$^F$7Qeol~c
zd3qQaSSD+;`ntdz45N2J86b~?0L%|CKFq-|zWLp(b->7;G?|N4mIY=iC(JbF$&IYy
hDlju3CNeO<%z*JXbFDO;Y#`wfz=#GI7#i#;!2qN=Wi9{!

delta 282
zcmbPd`oUm=j7ot70~i=UD26_Rk|2K%mmnsdElf&aF=pn84r`bvtmm5e-b0#!fr}NQ
zauQU7s0;%OkO@>(AT>FMaT1HMMULfUCZ-fdsEEPjjZ88^bD-L)pxPMJCvRleW>KmW
zFxt$=EWpTQATe2iWf6;P(^kgGVr)v2U$DrrRLp1nKRHHHmL-RQfoXEBq^~X`R6C5`
z#SAkKLbq@I^ugcR!<z@DgA=BbadN+eI7^*M)sxMJoKj4a4LBk=VR~0=R^;U8pX|Un
Jd9t9CEdc4;H0l5V

diff --git a/tests/test_projector.py b/tests/test_projector.py
index b784fdc..ca6fa33 100644
--- a/tests/test_projector.py
+++ b/tests/test_projector.py
@@ -250,7 +250,7 @@ class TestCaseYTiltProjector(unittest.TestCase):
 if __name__ == '__main__':
     suite = unittest.TestLoader().loadTestsFromTestCase(TestCaseSimpleProjector)
     unittest.TextTestRunner(verbosity=2).run(suite)
-    suite = unittest.TestLoader().loadTestsFromTestCase(TestCaseXTiltProjector)
-    unittest.TextTestRunner(verbosity=2).run(suite)
-    suite = unittest.TestLoader().loadTestsFromTestCase(TestCaseYTiltProjector)
-    unittest.TextTestRunner(verbosity=2).run(suite)
+#    suite = unittest.TestLoader().loadTestsFromTestCase(TestCaseXTiltProjector)
+#    unittest.TextTestRunner(verbosity=2).run(suite)
+#    suite = unittest.TestLoader().loadTestsFromTestCase(TestCaseYTiltProjector)
+#    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/test_projector/ref_mag_proj_x.nc b/tests/test_projector/ref_mag_proj_x.nc
index 025a3c9090a0e73cbf412772d985cfed6ee93b46..813872f91bfedb6f264ed5b83757c8afe0f935dd 100644
GIT binary patch
delta 112
zcmaE3@y24qY39jItcxe-u>`ZUci-ole3WG=n<>LZ(_1?y?_}OOS(PoAWzUM})X6n$
rODAWs@=rEkm(*lsfB-fq%?za(p)_26auNGpmP@aCGdCM^Sc(7u>LwhR

delta 112
zcmaE3@y24qY39jItcxe-u>`ZEr~kV<`6$a$HZz9FI_(^jcQS9CtjZS5!tpz;Z*mRW
q(#ct@{F4pXB{kU?Ab=H0Gec=cC=HjNT*SVYg<s!d#b#p;OA!E~{Te#}

-- 
GitLab