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