Forked from
empyre / empyre
427 commits behind the upstream repository.
phasemapper.py 9.29 KiB
# -*- coding: utf-8 -*-
"""Create and display a phase map from magnetization data."""
import numpy as np
# Physical constants
PHI_0 = 2067.83 # magnetic flux in T*nm²
H_BAR = 6.626E-34 # Planck constant in J*s
M_E = 9.109E-31 # electron mass in kg
Q_E = 1.602E-19 # electron charge in C
C = 2.998E8 # speed of light in m/s
def phase_mag_fourier(res, projection, b_0=1, padding=0):
'''Calculate phasemap from magnetization data (Fourier space approach).
Arguments:
res - the resolution of the grid (grid spacing) in nm
projection - projection of a magnetic distribution (created with pyramid.projector)
b_0 - magnetic induction corresponding to a magnetization Mo in T (default: 1)
padding - factor for zero padding, the default is 0 (no padding), for a factor of n the
number of pixels is increase by (1+n)**2, padded zeros are cropped at the end
Returns:
the phasemap as a 2 dimensional array
'''
v_dim, u_dim = np.shape(projection[0])
v_mag, u_mag = projection
# Create zero padded matrices:
u_pad = u_dim/2 * padding
v_pad = v_dim/2 * padding
u_mag_big = np.zeros(((1 + padding) * v_dim, (1 + padding) * u_dim))
v_mag_big = np.zeros(((1 + padding) * v_dim, (1 + padding) * u_dim))
u_mag_big[v_pad : v_pad + v_dim, u_pad : u_pad + u_dim] = u_mag
v_mag_big[v_pad : v_pad + v_dim, u_pad : u_pad + u_dim] = v_mag
# Fourier transform of the two components:
u_mag_fft = np.fft.fftshift(np.fft.rfft2(u_mag_big), axes=0)
v_mag_fft = np.fft.fftshift(np.fft.rfft2(v_mag_big), axes=0)
# Calculate the Fourier transform of the phase:
nyq = 1 / res # nyquist frequency
u = np.linspace( 0, nyq/2, u_mag_fft.shape[1])
v = np.linspace( -nyq/2, nyq/2, u_mag_fft.shape[0]+1)[:-1]
uu, vv = np.meshgrid(u, v)
coeff = (1j * res * b_0) / (2 * PHI_0)
phase_fft = coeff * (u_mag_fft*vv - v_mag_fft*uu) / (uu**2 + vv**2 + 1e-30)
# Transform to real space and revert padding:
phase_big = np.fft.irfft2(np.fft.ifftshift(phase_fft, axes=0))
phase = phase_big[v_pad : v_pad + v_dim, u_pad : u_pad + u_dim]
return phase
def phase_mag_real(res, projection, method, b_0=1, jacobi=None):
'''Calculate phasemap from magnetization data (real space approach).
Arguments:
res - the resolution of the grid (grid spacing) in nm
projection - projection of a magnetic distribution (created with pyramid.projector)
method - String, describing the method to use for the pixel field ('slab' or 'disc')
b_0 - magnetic induction corresponding to a magnetization Mo in T (default: 1)
jacobi - matrix in which to save the jacobi matrix (default: None, faster computation)
Returns:
the phasemap as a 2 dimensional array
'''
# Function for creating the lookup-tables:
def phi_lookup(method, n, m, res, b_0):
if method == 'slab':
def F_h(n, m):
a = np.log(res**2 * (n**2 + m**2))
b = np.arctan(n / m)
return n*a - 2*n + 2*m*b
return coeff * 0.5 * ( F_h(n-0.5, m-0.5) - F_h(n+0.5, m-0.5)
-F_h(n-0.5, m+0.5) + F_h(n+0.5, m+0.5) )
elif method == 'disc':
in_or_out = np.logical_not(np.logical_and(n == 0, m == 0))
return coeff * m / (n**2 + m**2 + 1E-30) * in_or_out
# Process input parameters:
v_dim, u_dim = np.shape(projection[0])
v_mag, u_mag = projection
coeff = -b_0 * res**2 / ( 2 * PHI_0 )
# Create lookup-tables for the phase of one pixel:
u = np.linspace(-(u_dim-1), u_dim-1, num=2*u_dim-1)
v = np.linspace(-(v_dim-1), v_dim-1, num=2*v_dim-1)
uu, vv = np.meshgrid(u, v)
phi_u = phi_lookup(method, uu, vv, res, b_0)
phi_v = phi_lookup(method, vv, uu, res, b_0)
# Calculation of the phase:
phase = np.zeros((v_dim, u_dim))
threshold = 0
if jacobi is not None: # With Jacobian matrix (slower)
jacobi[:] = 0 # Jacobi matrix --> zeros
############################### TODO: NUMERICAL CORE #####################################
for j in range(v_dim):
for i in range(u_dim):
phase_u = phi_u[v_dim-1-j:(2*v_dim-1)-j, u_dim-1-i:(2*u_dim-1)-i]
jacobi[:,i+u_dim*j] = phase_u.reshape(-1)
if abs(u_mag[j, i]) > threshold:
phase += u_mag[j,i] * phase_u
phase_v = phi_v[v_dim-1-j:(2*v_dim-1)-j, u_dim-1-i:(2*u_dim-1)-i]
jacobi[:,u_dim*v_dim+i+u_dim*j] = -phase_v.reshape(-1)
if abs(v_mag[j, i]) > threshold:
phase -= v_mag[j,i] * phase_v
############################### TODO: NUMERICAL CORE #####################################
else: # Without Jacobi matrix (faster)
for j in range(v_dim):
for i in range(u_dim):
if abs(u_mag[j, i]) > threshold:
phase += u_mag[j, i] * phi_u[v_dim-1-j:(2*v_dim-1)-j, u_dim-1-i:(2*u_dim-1)-i]
if abs(v_mag[j, i]) > threshold:
phase -= v_mag[j, i] * phi_v[v_dim-1-j:(2*v_dim-1)-j, u_dim-1-i:(2*u_dim-1)-i]
# Return the phase:
return phase
def phase_mag_real_alt(res, projection, method, b_0=1, jacobi=None): # TODO: Modify
'''Calculate phasemap from magnetization data (real space approach).
Arguments:
res - the resolution of the grid (grid spacing) in nm
projection - projection of a magnetic distribution (created with pyramid.projector)
method - String, describing the method to use for the pixel field ('slab' or 'disc')
b_0 - magnetic induction corresponding to a magnetization Mo in T (default: 1)
jacobi - matrix in which to save the jacobi matrix (default: None, faster computation)
Returns:
the phasemap as a 2 dimensional array
'''
# Function for creating the lookup-tables:
def phi_lookup(method, n, m, res, b_0):
if method == 'slab':
def F_h(n, m):
a = np.log(res**2 * (n**2 + m**2))
b = np.arctan(n / m)
return n*a - 2*n + 2*m*b
return coeff * 0.5 * ( F_h(n-0.5, m-0.5) - F_h(n+0.5, m-0.5)
-F_h(n-0.5, m+0.5) + F_h(n+0.5, m+0.5) )
elif method == 'disc':
in_or_out = np.logical_not(np.logical_and(n == 0, m == 0))
return coeff * m / (n**2 + m**2 + 1E-30) * in_or_out
# Function for the phase contribution of one pixel:
def phi_mag(i, j):
return (np.cos(beta[j,i])*phi_u[v_dim-1-j:(2*v_dim-1)-j, u_dim-1-i:(2*u_dim-1)-i]
-np.sin(beta[j,i])*phi_v[v_dim-1-j:(2*v_dim-1)-j, u_dim-1-i:(2*u_dim-1)-i])
# Function for the derivative of the phase contribution of one pixel:
def phi_mag_deriv(i, j):
return -(np.sin(beta[j,i])*phi_u[v_dim-1-j:(2*v_dim-1)-j, u_dim-1-i:(2*u_dim-1)-i]
+np.cos(beta[j,i])*phi_v[v_dim-1-j:(2*v_dim-1)-j, u_dim-1-i:(2*u_dim-1)-i])
# Process input parameters:
v_dim, u_dim = np.shape(projection[0])
v_mag, u_mag = projection
beta = np.arctan2(v_mag, u_mag)
mag = np.hypot(u_mag, v_mag)
coeff = -b_0 * res**2 / ( 2 * PHI_0 )
# Create lookup-tables for the phase of one pixel:
u = np.linspace(-(u_dim-1), u_dim-1, num=2*u_dim-1)
v = np.linspace(-(v_dim-1), v_dim-1, num=2*v_dim-1)
uu, vv = np.meshgrid(u, v)
phi_u = phi_lookup(method, uu, vv, res, b_0)
phi_v = phi_lookup(method, vv, uu, res, b_0)
# Calculation of the phase:
phase = np.zeros((v_dim, u_dim))
threshold = 0
if jacobi is not None: # With Jacobian matrix (slower)
jacobi[:] = 0 # Jacobi matrix --> zeros
############################### TODO: NUMERICAL CORE #####################################
for j in range(v_dim):
for i in range(u_dim):
phase_cache = phi_mag(i, j)
jacobi[:,i+u_dim*j] = phase_cache.reshape(-1)
if mag[j, i] > threshold:
phase += mag[j,i]*phase_cache
jacobi[:,u_dim*v_dim+i+u_dim*j] = (mag[j,i]*phi_mag_deriv(i,j)).reshape(-1)
############################### TODO: NUMERICAL CORE #####################################
else: # Without Jacobi matrix (faster)
for j in range(v_dim):
for i in range(u_dim):
if abs(mag[j, i]) > threshold:
phase += mag[j,i] * phi_mag(i, j)
# Return the phase:
return phase
#def phase_elec(mag_data, v_0=0, v_acc=30000):
# # TODO: Docstring
#
# res = mag_data.res
# z_dim, y_dim, x_dim = mag_data.dim
# z_mag, y_mag, x_mag = mag_data.magnitude
#
# phase = np.logical_or(x_mag, y_mag, z_mag)
#
# lam = H_BAR / np.sqrt(2 * M_E * v_acc * (1 + Q_E*v_acc / (2*M_E*C**2)))
#
# Ce = (2*pi*Q_E/lam * (Q_E*v_acc + M_E*C**2) /
# (Q_E*v_acc * (Q_E*v_acc + 2*M_E*C**2)))
#
# phase *= res * v_0 * Ce
#
# return phase