Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • empyre/empyre
  • weber/empyre
  • wessels/empyre
  • bryan/empyre
4 results
Show changes
Showing
with 1811 additions and 250518 deletions
# -*- coding: utf-8 -*-
"""
Created on Mon Apr 08 13:39:07 2013
@author: Jan
"""
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from numpy import pi
from PIL import Image
def holo_image(phase, res, density, title):
'''Display the cosine of the phasemap (times a factor) as a colormesh.
Arguments:
phase - the phasemap that should be displayed
res - the resolution of the phasemap
density - the factor for determining the number of contour lines
title - the title of the plot
Returns:
None
'''
# TODO: Docstring
img_holo = (1 + np.cos(density * phase * pi/2)) /2
phase_grad_y, phase_grad_x = np.gradient(phase, res, res)
phase_angle = (1 - np.arctan2(phase_grad_y, phase_grad_x)/pi) / 2 # used for colors
# TODO: look in semper code for implementation
phase_magnitude = np.sqrt(phase_grad_x ** 2 + phase_grad_y ** 2)
phase_magnitude /= np.amax(phase_magnitude)
phase_magnitude = np.sin(phase_magnitude * pi / 2)
cmap = plt.get_cmap('hsv')
rgba_img = cmap(phase_angle)
rgb_img = np.delete(rgba_img, 3, 2)
red, green, blue = rgb_img[:,:,0], rgb_img[:,:,1], rgb_img[:,:,2]
red *= 255.999 * img_holo * phase_magnitude
green *= 255.999 * img_holo * phase_magnitude
blue *= 255.999 * img_holo * phase_magnitude
rgb = np.dstack((red,green,blue)).astype(np.uint8)
img = Image.fromarray(rgb)
fig = plt.figure()
plt.imshow(img)
#plt.gca().invert_yaxis()
# cmap = my_cmap#plt.get_cmap('hsv')
# rgba_img = cmap(color_wheel_angle)
# rgb_img = np.delete(rgba_img, 3, 2)
# red, green, blue = rgb_img[:,:,0], rgb_img[:,:,1], rgb_img[:,:,2]
# red *= 255.999 * color_wheel_magnitude
# green *= 255.999 * color_wheel_magnitude
# blue *= 255.999 *color_wheel_magnitude
# rgb = np.dstack((red,green,blue)).astype(np.uint8)
#
# color_wheel = Image.fromarray(rgb)
#
# fig = plt.figure()
# plt.imshow(color_wheel)
# plt.gca().invert_yaxis()
#
#
# #rgb_img *= np.dstack((phase_magnitude, phase_magnitude, phase_magnitude))
#
# rgb = np.dstack((red,green,blue)).astype(np.uint8)
#
# img = Image.fromarray(rgb)
# img.save('myimg.jpeg')
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal')
plt.pcolormesh(phase_magnitude, cmap='gray')
ticks = ax.get_xticks()*res
ax.set_xticklabels(ticks.astype(int))
ticks = ax.get_yticks()*res
ax.set_yticklabels(ticks.astype(int))
ax.set_title(title+' - magnitude')
ax.set_xlabel('x-axis [nm]')
ax.set_ylabel('y-axis [nm]')
plt.colorbar()
plt.show()
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal')
plt.pcolormesh(phase_angle, cmap='hsv')
ticks = ax.get_xticks()*res
ax.set_xticklabels(ticks.astype(int))
ticks = ax.get_yticks()*res
ax.set_yticklabels(ticks.astype(int))
ax.set_title(title+' - angle')
ax.set_xlabel('x-axis [nm]')
ax.set_ylabel('y-axis [nm]')
plt.colorbar()
plt.show()
pass
cdict = {'red': [(0.0, 0.0, 0.0),
(0.5, 1.0, 1.0),
(1.0, 1.0, 1.0)],
'green': [(0.0, 0.0, 0.0),
(0.25, 0.0, 0.0),
(0.75, 1.0, 1.0),
(1.0, 1.0, 1.0)],
'blue': [(0.0, 0.0, 0.0),
(0.5, 0.0, 0.0),
(1.0, 1.0, 1.0)]}
my_cmap = mpl.colors.LinearSegmentedColormap('my_colormap',cdict,256)
def make_color_wheel():
x = np.linspace(-256, 256, num=512)
y = np.linspace(-256, 256, num=512)
xx, yy = np.meshgrid(x, y)
color_wheel_angle = (1 - np.arctan2(xx, -yy)/pi) / 2
r = np.sqrt(xx ** 2 + yy ** 2)
color_wheel_magnitude = (1 - np.cos(r * pi/360)) / 2
color_wheel_magnitude *= 0 * (r > 256) + 1 * (r <= 256)
cmap = plt.get_cmap('hsv')
rgba_img = cmap(color_wheel_angle)
rgb_img = np.delete(rgba_img, 3, 2)
red, green, blue = rgb_img[:,:,0], rgb_img[:,:,1], rgb_img[:,:,2]
red *= 255.999 * color_wheel_magnitude
green *= 255.999 * color_wheel_magnitude
blue *= 255.999 *color_wheel_magnitude
rgb = np.dstack((red,green,blue)).astype(np.uint8)
color_wheel = Image.fromarray(rgb)
plt.figure()
plt.imshow(color_wheel)
#plt.gca().invert_yaxis()
#fig = plt.figure()
#ax = fig.add_subplot(111, aspect='equal')
#plt.pcolormesh(color_wheel_angle, cmap='hsv')
#ax.set_title('Colorwheel')
#plt.colorbar()
#plt.show()
#
#fig = plt.figure()
#ax = fig.add_subplot(111, aspect='equal')
#plt.pcolormesh(color_wheel_magnitude, cmap='gray')
#ax.set_title('Colorwheel')
#plt.colorbar()
#plt.show()
\ No newline at end of file
File deleted
# -*- coding: utf-8 -*-
"""Create simple LLG Files which describe magnetization in 2D (z-Dim=1)."""
import numpy as np
import matplotlib.pyplot as plt
def slab(dim, (center, width)):
'''Get the magnetic shape of a slab.
Arguments:
dim - the dimensions of the grid, shape(y, x)
center - center of the slab in pixel coordinates, shape(y, x)
width - width of the slab in pixel coordinates, shape(y, x)
Returns:
the magnetic shape as a 2D-boolean-array.
'''
shape_mag = np.array([[abs(x - center[1]) < width[1] / 2
and abs(y - center[0]) < width[0] / 2
for x in range(dim[1])] for y in range(dim[0])])
return shape_mag
def disc(dim, (center, radius)):
'''Get the magnetic shape of a disc.
Arguments:
dim - the dimensions of the grid, shape(y, x)
center - center of the disc in pixel coordinates, shape(y, x)
radius - radius of the disc in pixel coordinates, shape(y, x)
Returns:
the magnetic shape as a 2D-boolean-array.
'''
shape_mag = np.array([[(np.sqrt((x - center[1]) ** 2 +
(y - center[0]) ** 2) < radius)
for x in range(dim[1])]
for y in range(dim[0])])
return shape_mag
def filament(dim, (pos, x_or_y)):
'''Get the magnetic shape of a single filament in x or y direction.
Arguments:
dim - the dimensions of the grid, shape(y, x)
pos - position of the filament (pixel coordinate)
x_or_y - string that determines the orientation of the filament
('y' or 'x')
Returns:
the magnetic shape as a 2D-boolean-array.
'''
shape_mag = np.zeros(dim)
if x_or_y == 'y':
shape_mag[:, pos] = 1
else:
shape_mag[pos, :] = 1
return shape_mag
def alternating_filaments(dim, (spacing, x_or_y)):
'''Get the magnetic shape of alternating filaments in x or y direction.
Arguments:
dim - the dimensions of the grid, shape(y, x)
spacing - the distance between two filaments (pixel coordinate)
x_or_y - string that determines the orientation of the filament
('y' or 'x')
Returns:
the magnetic shape as a 2D-boolean-array.
'''
shape_mag = np.zeros(dim)
if x_or_y == 'y':
for i in range(dim[1]):
if i % spacing == 0:
shape_mag[:, i] = 1 - 2 * (int(i / spacing) % 2) # 1 or -1
else:
for i in range(dim[0]):
if i % spacing == 0:
shape_mag[i, :] = 1 - 2 * (int(i / spacing) % 2) # 1 or -1
return shape_mag
def single_pixel(dim, pixel):
'''Get the magnetic shape of a single magnetized pixel.
Arguments:
dim - the dimensions of the grid, shape(y, x)
pixel - the coordinates of the magnetized pixel, shape(y, x)
Returns:
the magnetic shape as a 2D-boolean-array.
'''
shape_mag = np.zeros(dim)
shape_mag[pixel[0][0], pixel[0][1]] = 1
return shape_mag
def create_hom_mag(dim, res, beta, filename='output.txt',
plot_mag_distr=False, shape_fun=slab, *param):
'''Create homog. magnetization data, saved in a file with LLG-format.
Arguments:
dim - the dimensions of the grid, shape(y, x)
res - the resolution of the grid
(real space distance between two points)
beta - the angle of the magnetization
filename - the name of the file in which to save the data
shape_fun - the function to determine the shape of the magnetization,
several are defined in this module
*param - a set of parameters used in shape_fun
Returns:
the the magnetic shape as a 2D-boolean-array.
'''
res *= 1.0E-9 / 1.0E-2 # from nm to cm
x = np.linspace(res / 2, dim[1] * res - res / 2, num=dim[1])
y = np.linspace(res / 2, dim[0] * res - res / 2, num=dim[0])
xx, yy = np.meshgrid(x, y)
shape_mag = shape_fun(dim, param)
x_mag = np.array(np.ones(dim)) * np.cos(beta) * shape_mag
y_mag = np.array(np.ones(dim)) * np.sin(beta) * shape_mag
z_mag = np.array(np.zeros(dim))
if (plot_mag_distr):
fig = plt.figure()
fig.add_subplot(111, aspect='equal')
plt.quiver(x_mag, y_mag, pivot='middle', angles='xy', scale_units='xy',
scale=1, headwidth=6, headlength=7)
xx = np.reshape(xx,(-1))
yy = np.reshape(yy,(-1))
zz = np.array(np.ones(dim[0] * dim[1]) * res / 2)
x_mag = np.reshape(x_mag,(-1))
y_mag = np.reshape(y_mag,(-1))
z_mag = np.array(np.zeros(dim[0] * dim[1]))
data = np.array([xx, yy, zz, x_mag, y_mag, z_mag]).T
header = ('LLGFileCreator2D: %s\n %d %d %d' %
(filename.replace('.txt', ''), dim[1], dim[0], 1))
with open(filename,'w') as exportfile:
print >> exportfile, header
print >> exportfile, '\n'.join(' '.join('{:7.6e}'.format(cell)
for cell in row) for row in data)
\ No newline at end of file
File deleted
# -*- coding: utf-8 -*-
"""Create and display a phase map from magnetization data."""
import numpy as np
import matplotlib.pyplot as plt
PHI_0 = 2067.83 # magnetic flux in T*nm²
def fourier_space(mag_data, b_0=1, padding=0, v_0=0, v_acc=30000):
'''Calculate phasemap from magnetization data (fourier approach).
Arguments:
mag_data - MagDataLLG object (from magneticimaging.dataloader) storing
the filename, coordinates and magnetization in x, y and z
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
v_0 - average potential of the sample in V (default: 0)
v_acc - acceleration voltage of the microscop in V (default: 30000)
Returns:
the phasemap as a 2 dimensional array
'''
res = mag_data.res
x_dim, y_dim, z_dim = mag_data.dim
x_mag, y_mag, z_mag = mag_data.magnitude
# TODO: interpolation (redefine xDim,yDim,zDim) thickness ramp
x_pad = x_dim/2 * padding
y_pad = y_dim/2 * padding
x_mag_big = np.zeros(((1 + padding) * y_dim, (1 + padding) * x_dim))
y_mag_big = np.zeros(((1 + padding) * y_dim, (1 + padding) * x_dim))
x_mag_big[y_pad : y_pad + y_dim, x_pad : x_pad + x_dim] = x_mag
y_mag_big[y_pad : y_pad + y_dim, x_pad : x_pad + x_dim] = y_mag
x_mag_fft = np.fft.fftshift(np.fft.rfft2(x_mag_big), axes=0)
y_mag_fft = np.fft.fftshift(np.fft.rfft2(y_mag_big), axes=0)
nyq = 1 / res # nyquist frequency
x = np.linspace( 0, nyq/2, x_mag_fft.shape[1])
y = np.linspace( -nyq/2, nyq/2, x_mag_fft.shape[0]+1)[:-1]
xx, yy = np.meshgrid(x, y)
phase_fft = -(1j * b_0) / (2 * PHI_0) * ((x_mag_fft * yy - y_mag_fft * xx)
/ (xx ** 2 + yy ** 2 + 1e-18))
phase = np.fft.irfft2(np.fft.ifftshift(phase_fft, axes=0))
# TODO: Electrostatic Component
return phase
def real_space(mag_data):
'''Calculate phasemap from magnetization data (real space approach).
Arguments:
mag_data - MagDataLLG object (from magneticimaging.dataloader) storing
the filename, coordinates and magnetization in x, y and z
Returns:
the phasemap as a 2 dimensional array
'''
# TODO: Expand docstring!
pass # TODO: Implement!
def display(phase, res, title):
'''Display the phasemap as a colormesh.
Arguments:
phase - the phasemap that should be displayed
res - the resolution of the phasemap
title - the title of the plot
Returns:
None
'''
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal')
plt.pcolormesh(phase, cmap='Greys')
ticks = ax.get_xticks()*res
ax.set_xticklabels(ticks.astype(int))
ticks = ax.get_yticks()*res
ax.set_yticklabels(ticks.astype(int))
ax.set_title(title)
ax.set_xlabel('x-axis [nm]')
ax.set_ylabel('y-axis [nm]')
plt.colorbar()
plt.show()
def display_cos(phase, res, density, title):
'''Display the cosine of the phasemap (times a factor) as a colormesh.
Arguments:
phase - the phasemap that should be displayed
res - the resolution of the phasemap
density - the factor for determining the number of contour lines
title - the title of the plot
Returns:
None
'''
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal')
plt.pcolormesh(np.cos(density * phase), cmap='Greys')
ticks = ax.get_xticks()*res
ax.set_xticklabels(ticks.astype(int))
ticks = ax.get_yticks()*res
ax.set_yticklabels(ticks.astype(int))
ax.set_title(title+' - Cos')
ax.set_xlabel('x-axis [nm]')
ax.set_ylabel('y-axis [nm]')
plt.colorbar()
plt.show()
\ No newline at end of file
File deleted
# -*- coding: utf-8 -*-
"""
Created on Wed Apr 03 11:15:38 2013
@author: Jan
"""
import magneticimaging.magcreator as mc
import magneticimaging.dataloader as dl
import magneticimaging.phasemap as pm
import magneticimaging.holoimage as hi
from numpy import pi
def phase_from_mag():
'''Calculate and display the phase map from a given magnetization.
Arguments:
None
Returns:
None
'''
# TODO: Input via GUI
filename = 'output.txt'
b_0 = 1.0 # in T
v_0 = 0 # TODO: units?
v_acc = 30000 # in V
padding = 0
density = 1
dim = (500, 500) # in px (y,x)
res = 10 # in nm
beta = pi/4
shape_fun = mc.disc
# Slab:
center = (250, 250) # in px (y,x)
width = (200, 200) # in px (y,x)
# Disc:
radius = 100
# Filament:
pos = 5
# Alternating Filaments:
spacing = 5
# Single Pixel
pixel = (5, 5)
plot_mag_density = False
# create magnetic distribution:
mc.create_hom_mag(dim, res, beta, filename, plot_mag_density,
shape_fun, center, radius)
# load magnetic distribution:
mag_data = dl.MagDataLLG(filename)
# calculate phase map:
phase = pm.fourier_space(mag_data, b_0, padding)
# display phase map:
pm.display(phase, mag_data.res, 'Fourier Space Approach')
# display cosine of the phase map:
pm.display_cos(phase, mag_data.res, density, 'Fourier Space Approach')
hi.holo_image(phase, mag_data.res, density, 'Fourier Space Approach')
hi.make_color_wheel()
if __name__ == "__main__":
phase_from_mag()
\ No newline at end of file
This diff is collapsed.
|pipeline|_ |coverage|_ |pypi|_
.. |pipeline| image:: https://iffgit.fz-juelich.de/empyre/empyre/badges/master/pipeline.svg
.. _pipeline: https://iffgit.fz-juelich.de/empyre/empyre/commits/master
.. |coverage| image:: https://iffgit.fz-juelich.de/empyre/empyre/badges/master/coverage.svg
.. _coverage: https://iffgit.fz-juelich.de/empyre/empyre
.. |pypi| image:: https://badge.fury.io/py/empyre.svg
.. _pypi: https://pypi.org/project/empyre/
EMPyRe is an open source framework for constructing and solving hyperdimensional inverse problems and visualizing the corresponding input and output data. it has
its roots in the reconstruction of three-dimensional magnetization distributions from magnetic phase images generated by electron holography but is meant to be
extendable for many other problems that are based on forward models that are expressable as a combination of linear subproblems. EMPyRe is purely Python
package, so all platforms should be supported.
Installation
------------
EMPyRe is available on the `Python Package Index <http://pypi.python.org/pypi>`_ and can be simply be installed via `pip <http://pypi.python.org/pypi/pip>`_:
.. code-block:: bash
$ pip install empyre
Per default, only the strictly required libraries are installed, but there are a few additional dependencies that will unlock additional capabilites of EMPYRE.
* ``io`` will install the `tvtk <http://docs.enthought.com/mayavi/tvtk/README.html>`_ & `HyperSpy <https://hyperspy.org/>`_ packages that are used for loading and saving additional file formats.
.. warning::
Due to this `issue <https://github.com/hyperspy/hyperspy/issues/2315>`_, a pip install of hyperspy is currently not possible. Please use
`conda <https://docs.conda.io/en/latest/>`_ to install HyperSpy, instead.
* ``fftw`` will install `PyFFTW <https://github.com/pyFFTW/pyFFTW>`_ to speed up Fourier transforms used in some forward models.
* ``colors`` will install the `cmocean <https://matplotlib.org/cmocean/>`_, whose ``balance`` color map is used as a default for the ``imshow`` commmand, if available.
* ``3d`` will install the `mayavi <https://docs.enthought.com/mayavi/mayavi/>`_ package for 3D plotting capabilities.
* ``tests`` will install all dependencies that are needed to test the package (usually not necessary for the average user).
* ``all`` will install all of the dependencies listed above.
You can choose these settings by using, *e.g.*:
.. code-block:: bash
$ pip install empyre[all]
Structure
---------
EMPyRe has several dedicated modules which are fully documented `here <https://empyre.iffgit.fz-juelich.de/empyre/>`_!
* The ``fields`` module provides the ``Field`` container class for multidimensional scalar or vector fields and is the fundamental data structure used in EMPyRe.
* The ``vis`` module enables the plotting of ``Field`` objects, based on and similar in syntax to the commonly known `matplotlib <https://matplotlib.org/>`_ framework.
* The ``models`` module provides tools for constructing forward models that describe processes in Electron Microscopy.
* The ``reconstruct`` module is a collection of tools for solving the inverse problems corresponding to the constructed forward models and diagnostic tools for their assessment.
* The ``io`` module is used to load and save ``Field`` objects and the models generated by the ``models`` subpackage.
* The ``utils`` module, which houses utility functionality used throughout EMPyRe.
License
-------
EMPyRe is licensed under `GPLv3 <https://iffgit.fz-juelich.de/empyre/empyre/-/blob/master/LICENSE>`_.
[
{
"displayname": "Jan Caron",
"authorname": "Caron, Jan",
"affiliation": "Jülich Research Centre, Ernst Ruska Centre (ER-C)",
"gitlab": "jan-car",
"contribution": "Design, implementation, documentation, testing, management, majority of the code",
"orcid": "0000-0002-0873-889X"
},
{
"displayname": "Jörn Ungermann",
"authorname": "Ungermann, Jörn",
"affiliation": "Jülich Research Centre, Institute of Energy and Climate Research - Stratosphere (IEK-7)",
"gitlab": "unger",
"contribution": "Discussions, structure, reconstruction code from jutil",
"orcid": "0000-0001-9095-8332"
},
{
"displayname": "Alexander Clausen",
"authorname": "Clausen, Alexander",
"affiliation": "Jülich Research Centre, Ernst Ruska Centre (ER-C)",
"gitlab": "clausen",
"contribution": "Discussions, structure, testing",
"orcid": "0000-0002-9555-7455"
},
{
"displayname": "Dieter Weber",
"authorname": "Weber, Dieter",
"affiliation": "Jülich Research Centre, Ernst Ruska Centre (ER-C)",
"gitlab": "weber",
"contribution": "Discussions, structure, testing",
"orcid": "0000-0001-6635-9567"
},
{
"displayname": "Teresa Weßels",
"authorname": "Weßels, Teresa",
"affiliation": "Jülich Research Centre, Ernst Ruska Centre (ER-C)",
"gitlab": "wessels",
"contribution": "Discussions, testing",
"orcid": "0000-0001-5526-639X"
},
{
"displayname": "Matthew Bryan",
"authorname": "Bryan, Matthew",
"affiliation": "CEA-Leti",
"gitlab": "bryan",
"orcid": "0000-0001-9134-384X",
"contribution": "Small bugfixes, CI and conda packaging"
}
]
This diff is collapsed.
# -*- coding: utf-8 -*-
# flake8:noqa
# %% [markdown]
## Demo 1): First steps and the Field class
# In this first demo notebook, you'll learn how to import the `empyre` package and how to utilize the `Field` class,
# which can contain multidimensional vector and scalar fields and is core to many functions of `empyre`. If you haven't
# already, please visit the [documentation](https://empyre.iffgit.fz-juelich.de/empyre/) to see how to install `empyre`.
# %% [markdown]
### Import:
# First import `empyre`, we recommend to use the abbreviation `emp`. We'll numpy as well.
# %%
import empyre as emp
import numpy as np
# %% [markdown]
### Represent your data with the 'Field' class
# A `Field` object is a container for multi-dimensional vector or scalar fields, each Field has three parameters:
#
# * **data**: Your underlying data, *e.g.* as a numpy array.
#
# * **scale**: The axis scaling along each axis, given as a tuple. `scale` can be a given as a single number during
# construction of the `Field` object, which assumes that the scale is the same for all axes.
#
# * **vector**: Determines if the field is a scalar field (`vector=False`, which is the default) or a vector field
# (`vector=True`). Please note that, if `vector=True`, the last axis of `data` is interpreted as the dimension of the
# vector components. The component dimension is the last one, so that the `Field` class can be indexed similarily to
# numpy arrays. This way, indexing a field with just spatial indices (by dropping the trailing component dimension)
# will work on scalar and vector fields at the same time. Most functions of `Field` are designed to work on both
# vector and scalar fields, as well.
#
#
# We'll start with a 2D scalar and a 2D vector field. You'll find the base `Field` class in the `fields` submodule.
# Note that you can print fields to get a short summary about their parameters.
# %%
scalar_data = np.zeros((16, 16))
scalar_field = emp.fields.Field(data=scalar_data, scale=1, vector=False)
print(scalar_field)
vector_data = np.zeros((16, 16, 3))
vector_field = emp.fields.Field(data=vector_data, scale=1, vector=True)
print(vector_field)
# %% [markdown]
### Create more interesting fields with `create_...` functions
# The two fields created before are empty and not very interesting. The `fields` submodule has a few alternate
# constructor functions to generate shapes and vector fields in particular.
# Let's look at the `shapes` submodule first, whose documentation can be found
# [here](https://empyre.iffgit.fz-juelich.de/empyre/fields.html#module-empyre.fields.shapes) (Note that all functions
# can be directly accessed via `emp.fields.create_...`, no need to access `shapes` directly).
# A simple example is the disc shape:
# %%
field_disc = emp.fields.create_shape_disc(dim=(32, 32), radius=10)
print(field_disc)
# %% [markdown]
### Visualize fields with the 'vis' subpackage
# The `vis` subpackage is used to visualize field objects. Its interface mirrors that of matplotlib and should be easy
# to learn if you are familiar with the latter. You can find the documentation
# [here](https://empyre.iffgit.fz-juelich.de/empyre/vis.html).
# Usually, scalar fields can be visualised with the `imshow` function (additional *kwargs* will be forwarded to the
# underlying matplotlib function, a behaviour shared by almost all 2D plot functions in `vis`):
#
# %%
emp.vis.imshow(field_disc)
# %% [markdown]
### Some `vis` basics
# The `vis` package has a lot of decorators that you can use to enhance your plots, you can find the documentation
# [here](https://empyre.iffgit.fz-juelich.de/empyre/vis.html#module-empyre.vis.decorators).
# To apply them to a plot, just use the commands in the lines following a plot command. To generate a new plot, use the
# `vis.new()` function. If you are in a Jupyter notebook, like here, you don't need `new` at the beginning of each cell,
# but `new` has some other neat features (like returning a grid of subplots) that could be useful for your specific
# case. Furthermore, `empyre` uses custom matplotlib stylesheets for 'images' and 'plots', respectively, which you can
# explicitely set in the `new` function with the `mode` parameter. For publications, `new` can specify the exact size
# of your plots according to your LaTeX document and save them afterwards with the `savefig` function.
# See [here](https://empyre.iffgit.fz-juelich.de/empyre/vis.html#empyre.vis.tools.new) for more info on all of those
# features. `empyre` provides mainly plotting routines for images, but you can mix and match with matplotlib routines,
# for plots.
# %%
emp.vis.new(mode='image') # Not necessary here, just for clarity, 'image' is the default!
emp.vis.imshow(field_disc)
emp.vis.scalebar()
# Create a new figure with two plots
fig, axes = emp.vis.new(nrows=1, ncols=2, aspect=0.3, mode='plot')
x = np.arange(10)
axes[0].plot(x, np.random.rand(10))
axes[1].plot(x, np.random.rand(10))
# %% [markdown]
### Creation of vector fields
# The `fields` module can not only create shapes but also some special vector fields like homogeneous distributions
# (all vectors point in the same direction) or vortices / skyrmions, all of which you can find
# [here](https://empyre.iffgit.fz-juelich.de/empyre/fields.html#module-empyre.fields.vectors).
# The according functions all start with `emp.vis.create_vector_...`.
# `vis` also has special plotting functions for vector fields, often with directional color encoding, where the vectors
# are colored according to:
# * **hue**: encodes the in-plane direction of the vector.
#
# * **brightness**: encodes the out-of-plane component of the vector (light: up, dark: down)
#
# * **saturation**: encodes the amplitude of the vector with central gray corresponding to an amplitude of zero.
#
# One example for such a plot is the `colorvec` funtion. You can always add the colorwheel to your plots with the
# `colorwheel` function, as well. Let's try all of this out with a vortex vector distribution with an out-of-plain core
# with a radius of 3 pixels, pointing upwards.
# %%
field_vortex = emp.fields.create_vector_vortex(dim=(32, 32), core_r=3, oop_r=5)
print(field_vortex)
# And plot (colorwheel shows directional color encoding):
emp.vis.colorvec(field_vortex)
emp.vis.scalebar()
emp.vis.colorwheel()
# %% [markdown]
### Quiver plot
# Another way of plotting vector fields is provided by the `quiver` plot function (using the matplotlib function of the
# same name under the hood). `quiver` automatically reduces the number of drawn arrows for clarities sake. Have a look
# into the function documentation to learn more.
# %%
emp.vis.quiver(field_vortex)
emp.vis.scalebar()
emp.vis.colorwheel()
# %% [markdown]
### Combining vector and shapes
# The `Field` class implements many basic math operators like `+`, `-` or `*`. The latter can be used to combine a
# constructed vector field and a shape (that acts like a mask in this case). Furthermore, you can combine `colorvec` and
# `quiver` plots to create appealing images.
# %%
field_comb = field_disc * field_vortex
emp.vis.colorvec(field_comb)
emp.vis.quiver(field_comb)
emp.vis.scalebar()
emp.vis.colorwheel()
# %% [markdown]
### Congratulations
# You managed to create and plot your first 2D vector fields! Look at the functions docstrings or the documentation of
# `empyre` to learn more or have a look at the next demos to find out how to handle 3D fields and more functionality of
# the `Field` class.
\ No newline at end of file
This diff is collapsed.
# -*- coding: utf-8 -*-
# flake8:noqa
# %% [markdown]
## Demo 2): Handling 3-dimensional fields
# In this demo, you'll learn the intricacies of how to handle and especially how to plot 3-dimensional fields, the
# specific pitfalls during plotting and how to avoid them.
# %% [markdown]
### Import:
# %%
import empyre as emp
import logging
logger = logging.getLogger()
logger.setLevel(logging.WARNING)
# %% [markdown]
### Create a 3D vortex
# Similar to the first demo, we create a vortex distribution and the shape of a disc, which we'll combine to generate a
# `Field` object (note that you can also use compound assignment operators like `*=`). This time, however, we generate
# a 3D distribution by using a shape of `(32, 32, 32)` for the dimension.
# %%
field = emp.fields.create_vector_vortex(dim=(32, 32, 32), core_r=3, oop_r=5)
field *= emp.fields.create_shape_disc(dim=(32, 32, 32), radius=10)
# %% [markdown]
### How to plot 3D distributions
# If you try to use:
#
# `emp.vis.colorvec(field)`
#
# for plotting, you'll get an error, telling you that you can only plot 2 dimensions (which makes sense for all 2D
# matplotlib plots). Instead of plotting the whole 3D volume, we can instead plot slices of it by using indices with
# the usual numpy bracket notation on our `Field` objects. Let's try to plot a central slice along the z-direction.
# Note that the `field_slice_z` object is only two-dimensional with shape `(32, 32)`!
# %%
field_slice_z = field[15, ...]
emp.vis.colorvec(field_slice_z)
print(field)
print(field_slice_z)
# %% [markdown]
### Slices along other directions
# Next, let's slice along the y-direction of the 3D volume (let's again take a central slice through the volume) and
# compare the two slices. The dashed line in the left image shows, where the slice in the right image lies. You'll
# notice that the color encoding in both images corresponds to the 3D directions of the vectors in our original 3D
# volume. This is due to the fact that our 2D slice still has information about the 3 components of the vector field.
# Furthermore, we'll use some more decoration functions of `vis`, which should be self explanatory.
# %%
fig, axes = emp.vis.new(1, 2)
field_slice_y = field[:, 15, :]
emp.vis.colorvec(field_slice_z, axis=axes[0])
emp.vis.colorwheel(axis=axes[0])
emp.vis.annotate('z-slice', axis=axes[0])
emp.vis.coords(coords=('x', 'y'), axis=axes[0])
axes[0].axhline(y=16, linestyle='--')
emp.vis.colorvec(field_slice_y, axis=axes[1])
emp.vis.annotate('y-slice', axis=axes[1])
emp.vis.coords(coords=('x', 'z'), axis=axes[1])
print(field)
print(field_slice_y)
# %% [markdown]
### Quiver and its ambiguity in 3D
# Let's try the exact same plot with `quiver` instead of `colorvec` now. Note that we use the `color_angles=True` mode,
# which let's us color the insides of the arrows the same way colorvec would. While the z-slice works finde, the y-slice
# may show the right colors, but the direction of the arrows are wrong. This is due to the fact that `colorvec` takes
# the information about all three vector components into account for coloring the pixels. This 3D color information is
# the same, however you slice the 3D volume (the vortex core will always point in positive z-direction, corresponding to
# white coloration). The arrows plotted by `quiver`, however have to be adapted to the current slicing plane and therefore
# need information about what this plane is! Note that `field_slice_y` has the shape `(32, 32)` and therefore is only
# 2-dimensional. `quiver` does not know how to assign the 3 vector components correctly to only 2 dimensions. To solve
# this ambiguity, `quiver` will only take the first two components, which erroneously interprets the slice as an
# x-y-plane. `quiver` will, however, warn the user about ambiguous assignments like this, if the number of vector
# components is larger than the number of dimensions.
# %%
fig, axes = emp.vis.new(1, 2)
field_slice_y = field[:, 15, :]
emp.vis.quiver(field_slice_z, color_angles=True, axis=axes[0])
emp.vis.colorwheel(axis=axes[0])
emp.vis.annotate('z-slice', axis=axes[0])
emp.vis.coords(coords=('x', 'y'), axis=axes[0])
axes[0].axhline(y=16, linestyle='--')
emp.vis.quiver(field_slice_y, color_angles=True, axis=axes[1])
emp.vis.annotate('y-slice', axis=axes[1])
emp.vis.coords(coords=('x', 'z'), axis=axes[1])
print(field)
print(field_slice_y)
# %% [markdown]
### How to correctly use quiver in 3D
# We can solve the ambiguity, by providing `quiver` with the information about which slice we really want to show. This
# can be done by slicing differently. Instead of using a single number to specify the slice in the y-direction, we use
# a slicing object (notation `15:16` in the example, or `to:from` in general). This way, the resulting field object will
# retain its 3-dimensional shape, but with a length of 1 along the slicing axis (here: `(32, 1, 32)`). In general, all
# 2D plotting routines can handle higher dimensional slices, as long as you can "squeeze" the fields to 2D by discarding
# dimensions of size 1. This is exaclty what is done in `quiver`, as well, but by providing the info about which axes
# are squeezed at the start of the function, `quiver` can now which vector components it should take to correctly plot
# the slice that the user wanted to plot. Note how in this example, the arrows in the vortex core are correctly pointing
# in positive z-direction (and are colored correctly, as well).#
# %%
fig, axes = emp.vis.new(1, 2)
field_slice_z_correct = field[15:16, ...]
field_slice_y_correct = field[:, 15:16, :]
emp.vis.quiver(field_slice_z_correct, color_angles=True, axis=axes[0])
emp.vis.colorwheel(axis=axes[0])
emp.vis.annotate('z-slice', axis=axes[0])
emp.vis.coords(coords=('x', 'y'), axis=axes[0])
axes[0].axhline(y=16, linestyle='--')
emp.vis.quiver(field_slice_y_correct, color_angles=True, axis=axes[1])
emp.vis.annotate('y-slice', axis=axes[1])
emp.vis.coords(coords=('x', 'z'), axis=axes[1])
print(field)
print(field_slice_y)
print(field_slice_y_correct)
# %% [markdown]
### Combined plot
# Let's show `colorvec` and `quiver` in combination to generate a nice plot. Note that the points in the y-slice image
# (purple and green background) correspond to arrows in positive and negative y-direction (compare with the z-slice
# image) and are therefore out-of-plane for this specific slice.
# %%
fig, axes = emp.vis.new(1, 2)
emp.vis.colorvec(field_slice_z_correct, axis=axes[0])
emp.vis.quiver(field_slice_z_correct, axis=axes[0])
emp.vis.colorwheel(axis=axes[0])
emp.vis.annotate('z-slice', axis=axes[0])
emp.vis.coords(coords=('x', 'y'), axis=axes[0])
axes[0].axhline(y=16, linestyle='--')
emp.vis.colorvec(field_slice_y_correct, axis=axes[1])
emp.vis.quiver(field_slice_y_correct, axis=axes[1])
emp.vis.annotate('y-slice', axis=axes[1])
emp.vis.coords(coords=('x', 'z'), axis=axes[1])
# %% [markdown]
### Conclusion
# This demo showed you how to correctly slice higher dimensional `Field` objects to utilize the 2D plot functions.
# In the next demo, we'll see how we can load experimental data and how to use the many manipulation and transformation
# tools, that come with the `Field` class.
This diff is collapsed.
# -*- coding: utf-8 -*-
# flake8:noqa
# %% [markdown]
## Demo 3): File IO and basic field manipulation
# In this demo, you'll learn how to import your own data, more plotting routines and how to postprocess data
# with the inherent functionality of the `Field` class.
# %% [markdown]
### Import:
# %%
import empyre as emp
import os
import logging
logger = logging.getLogger()
logger.setLevel(logging.WARNING)
# %% [markdown]
### Loading data
# You can use the `io` module (documentation will follow soon) to load a variety of different data formats. It uses
# the HyperSpy package (see [here](https://hyperspy.org/) for more info) for a lot of the underlying work, but also has
# some routines for other formats as well. In general, all image files should work, as well as numpy data and others.
# The example here is the magnetic structure of a litographic Cobalt pattern. Note how we have to specify, that we want
# to load a vector field by setting `vector=True`. For vector fields, we can also set, which axis should be interpreted
# as the component axis (default is `comp_pos=-1` for the last axis).
# %%
field_io = emp.io.load_field(os.path.join('files', 'magdata.hdf5'), vector=True, comp_pos=0)
print(field_io)
# %% [markdown]
### Scaling data
# Note how the loaded field is quite large (512 x 512 pixels). You can manipulate the size of your fields with the `bin`
# function, to bin over a certain number of pixels, or use `zoom` to interpolate to higher numbers of pixels. Here,
# we'll do the former. Note how the scale automatically changes after binning.
# %%
field = field_io.bin(3)
print(field)
# %% [markdown]
### Plot the loaded field
# We now want to plot our loaded data. Note how the scalebar always tries to show "nice" numbers, even though the scale
# itself had long floating points. We are not content with the quiver plot though.
# %%
emp.vis.colorvec(field)
emp.vis.quiver(field)
emp.vis.colorwheel()
emp.vis.scalebar()
# %% [markdown]
### Nicer plot
# The default binning size of `quiver` is determined in a way that tries to show 16 arrows across the whole field of
# view. In this case, we want to tweak this number to show more, but smaller arrows, which can be done with the `n_bin`
# parameter.
# %%
emp.vis.colorvec(field)
emp.vis.quiver(field, n_bin=4)
emp.vis.colorwheel()
emp.vis.scalebar()
# %% [markdown]
### Split vector fields into its components as scalar fields
# Furthermore, we can split our vector fields into its scalar components (also returned as fields), by accessing the
# `comp` attribute. We'll also plot the in-plane components here.
# %%
x_field, y_field, z_field = field.comp
print(x_field)
fig, axes = emp.vis.new(1, 2)
emp.vis.imshow(x_field, axis=axes[0])
emp.vis.scalebar(axis=axes[0])
emp.vis.imshow(y_field, axis=axes[1])
# %% [markdown]
### Merge scalars into vector fields
# The opposite can be done as well, by using the class method `from_scalar_fields` of the `Field` class as an alternate
# constructor.
# %%
field_recombined = emp.fields.Field.from_scalar_fields([x_field, y_field, z_field])
print(field_recombined)
# %% [markdown]
### Load more data and combine them
# Let's load more data from different formats, in this case the magnetic phase map that results from the magnetisation
# distribution from above, as well as a mask that determines the position of the litographic pattern and a "confidence"
# array that shows which regions of the (experimentally acquired) phase image we want to trust. Note that some loading
# routines need to be provided with the `scale`, otherwise they would be defaulting to 1, as not all formats are capable
# of saving the scale correctly. All fiels are scalar fields and thus don't need the `vector` parameter.
# %%
field_phase = emp.io.load_field(os.path.join('files', 'phase.unf'))
field_mask = emp.io.load_field(os.path.join('files', 'mask.png'), scale=field_phase.scale)
field_conf = emp.io.load_field(os.path.join('files', 'confidence.npy'), scale=field_phase.scale)
# Plots
fig, axes = emp.vis.new(1, 3)
emp.vis.imshow(field_phase, axis=axes[0])
emp.vis.annotate('phase', axis=axes[0])
emp.vis.imshow(field_mask, axis=axes[1])
emp.vis.annotate('mask', axis=axes[1])
emp.vis.imshow(field_conf, axis=axes[2])
emp.vis.annotate('confidence', axis=axes[2])
# %% [markdown]
### Combined plot
# We now want to plot all the loaded information into the same image.
# * We plot the phase with `imshow`, just like before.
# * We plot the mask as a `contour` plot.
# * We plot the confidence as another `imshow`, but with a special colormap.
# * We add a colorbar that needs the phase image as an input to work.
# * We add a scalebar.
# %%
im = emp.vis.imshow(field_phase)
emp.vis.contour(field_mask)
emp.vis.imshow(field_conf, cmap=emp.vis.colors.cmaps.transparent_confidence)
emp.vis.colorbar(im, label='phase [rad]')
emp.vis.scalebar()
# %% [markdown]
### Holographic contour plots
# Magnetic phase images are often shown as holographic contour maps, which you get by calculating the curl (or rotation)
# of the magnetic phase and overlay it with cosine contours (the cosine of the phase, amplified by a gain factor). Due
# to `empyre` being rooted in the analysis of magnetic phase images, the according functionality is here as well. First,
# let's create the rotation of the phase image by using the `curl` function. Visualizing it with `colorvec` shows a
# problem, though. The strongest saturation is fixed to obviously erroneous pixels and the interesting parts of the
# image seem faded out and unsaturated.
# %%
emp.vis.colorvec(field_phase.curl())
# %% [markdown]
### The curl and clipping
# We can use the `clip` function to clip the values to all lie in a Gaussian distribution with a certain `sigma`
# (the usual numpy functionality with specifying `vmin` and `vmax` is available as well). This clips noisy regions where
# the rotation is very strong due to sharp boundaries or noise.
# %%
emp.vis.colorvec(field_phase.curl().clip(sigma=2))
# %% [markdown]
### Cosine contours
# Next we look at the cosine contours of the phase, which we can plot via the `cosine_contours` plot function in `vis`.
# Not that normally, a `gain` factor is determined automatically (default: `gain='auto'`), but we'll look at the pure,
# unaltered cosine here first.
# %%
emp.vis.cosine_contours(field_phase, gain=1)
# %% [markdown]
### The full contour map
# Now let's combine both plots, add confidence and mask plots again, as well as a colorwheel and a scalebar.
# The cosine contour now searches for an adequate gain automatically. Also try out other colormaps (e.g. the
# `cyclic_classic` one that corresponds to many holographic contour plots in older publications)
# %%
cmap = emp.vis.colors.cmaps.cyclic_cubehelix
emp.vis.colorvec(field_phase.curl().clip(sigma=2), cmap=cmap)
emp.vis.cosine_contours(field_phase)
emp.vis.contour(field_mask, colors='w')
emp.vis.imshow(field_conf, cmap=emp.vis.colors.cmaps.transparent_confidence)
emp.vis.colorwheel(cmap=cmap)
emp.vis.scalebar()
File added
File added
demos/files/mask.png

2.37 KiB

File added