diff --git a/pyramid/config.py b/pyramid/config.py index 9f45e1f5537f2f5506271c1d5e1ac2f523660e79..c0d9db4e80c7588aab76783d1c2cf55d13bd76e7 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 d7eae39f3a6644b8c301c41afabe062596f4c239..7b3945a2ebccf7e67262be301895aef160219276 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 ca322250a310d23b7b55773cee41d342cc54d715..f493b056fd32a7a1a642416422c9cb66201fc001 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 936e4e580af9bbbab6e26c1bdcaf4342ae2fcaa1..f2f9fb41f15a0d0946172115d8b8cb4b53bb9ee1 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 047c2758cbd75a71e7f3b2c9474436979a9cb72a..9dddb7254ea1255b8f22d650f58fc69e1df84dc9 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 a30b91fdb3f058c8adb2acf74cd790ed82cefaef..ef845e9310e668d5470a61100d09900fd8d1abc9 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 bbc3afc970ce7efcbae3a8c2ac5f563c62cdfd1c..6cf8163c9c54cf3c87ea75663823638a2ba29579 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 1ded00a99736e81d2901e9e2299a06db17150939..cd3af7a76126563abe299daba7acc4f0f67e4976 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 Binary files a/tests/test_costfunction/phase_map_ref.nc and b/tests/test_costfunction/phase_map_ref.nc differ diff --git a/tests/test_dataset.py b/tests/test_dataset.py index 4f06f8203f8507834b685ebfda391012f531d601..2956acf9f95fd67f8b187eb56ce49c90dcb079fd 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 Binary files a/tests/test_forwardmodel/phase_map_ref.nc and b/tests/test_forwardmodel/phase_map_ref.nc differ diff --git a/tests/test_magdata.py b/tests/test_magdata.py index a178de31891a93609248c941caa5e48032d89499..8b189d250746c789f1535ec20dcf0494fb2c9fd7 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 Binary files /dev/null and b/tests/test_magdata/mag_data_flipx.nc differ 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 Binary files /dev/null and b/tests/test_magdata/mag_data_flipy.nc differ 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 Binary files /dev/null and b/tests/test_magdata/mag_data_flipz.nc differ 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 Binary files /dev/null and b/tests/test_magdata/mag_data_orig.nc differ 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 Binary files /dev/null and b/tests/test_magdata/mag_data_ref_load.nc differ 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 0000000000000000000000000000000000000000..293c185a73fce0669baba800745f16fcc48d7a2e --- /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 Binary files /dev/null and b/tests/test_magdata/mag_data_rotx.nc differ 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 Binary files /dev/null and b/tests/test_magdata/mag_data_roty.nc differ 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 Binary files /dev/null and b/tests/test_magdata/mag_data_rotz.nc differ diff --git a/tests/test_phasemap.py b/tests/test_phasemap.py index 472222d8e92e3b4c6571850c88314670c985493b..411b7511decaee7d14c3a7699a7f7c051b42a773 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 Binary files a/tests/test_phasemap/ref_phase_map.nc and b/tests/test_phasemap/ref_phase_map.nc differ diff --git a/tests/test_phasemapper/phase_map.nc b/tests/test_phasemapper/phase_map.nc index a2c2ae7e47b80c712b796fb8bba36a84403fa3a0..dbe3234498122e809bda8ee5e062cc54f43c456b 100644 Binary files a/tests/test_phasemapper/phase_map.nc and b/tests/test_phasemapper/phase_map.nc differ diff --git a/tests/test_phasemapper/phase_map_fc.nc b/tests/test_phasemapper/phase_map_fc.nc index 2d614499502b8fa35af42c961d6ce4b971afdb58..ea13e499169e9b8c32baead2c3f5baee40e075ef 100644 Binary files a/tests/test_phasemapper/phase_map_fc.nc and b/tests/test_phasemapper/phase_map_fc.nc differ diff --git a/tests/test_projector.py b/tests/test_projector.py index b784fdce044e54c3671f52f874cacc32fb10bfba..ca6fa33d635b73f08e1120f9a0bc90d98216c709 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 Binary files a/tests/test_projector/ref_mag_proj_x.nc and b/tests/test_projector/ref_mag_proj_x.nc differ