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
Commits on Source (416)
Showing
with 2674 additions and 11 deletions
!.gitignore
!.gitlab-ci.yml
.*
desktop.ini
*.pyc
*.egg-info
*.DS_Store
*.DS
*.ipynb_checkpoints*
build/*
docs/_build/*
dist/*
src/empyre/version.py
htmlcov/
coverage.xml
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: $CI_COMMIT_BRANCH
- if: $CI_COMMIT_TAG
before_script:
# Install requirements for empyre:
- source ${CONDA_DIR}/etc/profile.d/conda.sh
- conda create -q --name empyre # -q: quiet/ no progressbar, because it spams the log!
- conda activate empyre
- conda install -y python numpy sphinx pip setuptools jupyter pandoc uv
- uv pip install .[tests,docs]
- conda info --envs
# Install jutil via deploy token access: # TODO: still needed?
#- pip install git+https://empyre:"$JUTIL_DEPLOY_TOKEN"@jugit.fz-juelich.de/j.ungermann/jutil.git
stages:
- test
- deploy
test_style:
stage: test
image: condaforge/miniforge3:latest
script:
- pip install pre-commit
- pre-commit install
- pre-commit run --all-files
test_function:
stage: test
image: condaforge/miniforge3:latest
script:
# Execute all tests and also check coverage with --cov:
- pip install "numpy>=2"
- pytest --cov
artifacts:
paths:
- .coverage
expire_in: 1 week
test_docs:
stage: test
image: condaforge/miniforge3:latest
script:
# -f: Force overwriting of any existing generated files.
# -e: Put documentation for each module on its own page.
# -o: Directory to place the output files. If it does not exist, it is created.
# last parameter: module path
# - python setup.py clean # TODO: Simplest way to generate version.py without doing anything else?
#- sphinx-apidoc -f -e -o docs/api src/empyre
# Build the documentation from 'docs' and put into 'build/sphinx':
- sphinx-build docs build/sphinx -W # -w fails the build if warnings appear!
artifacts:
paths:
- build/sphinx
expire_in: 1 week
test_install:
stage: test
image: condaforge/miniforge3:latest
before_script: [] # before_script not needed here!
script:
- conda install -y "python<3.12"
- pip install .[all]
pages:
stage: deploy
image: busybox
before_script: [] # before_script not needed here!
script:
- mkdir public
- cp -r build/sphinx/* public/
# -r: recursive, also copies subfolders!
artifacts:
paths:
- public
rules:
- if: '$CI_COMMIT_BRANCH == "master" || $CI_COMMIT_TAG =~ /^\d+\.\d+(\.\d+)?$/'
pypi:
stage: deploy
image: condaforge/miniforge3:latest
before_script: [] # before_script not needed here!
script:
- pip install hatch twine
- hatch build
- twine upload -u __token__ -p $PYPI_ACCESS_TOKEN dist/* # -u user -p password upload_source
rules: # similar to only/except, but newer!
# Job is executed if branch is master AND if a tag is building which matches the regular expression!
# ONLY executes if commit to master has a tag, ^:start, $:end, valid example: "1.2.3", no "-dev" at the end!
- if: '$CI_COMMIT_TAG =~ /^\d+\.\d+(\.\d+)?$/'
File moved
<!--
Please read this!
Before opening a new issue, make sure to search for keywords in the issues
filtered by the "bug" label:
- https://iffgit.fz-juelich.de/empyre/empyre/issues?label_name%5B%5D=bug
and verify the issue you're about to submit isn't a duplicate.
-->
### Summary
<!-- Summarize the bug encountered concisely. -->
### Steps to reproduce
<!-- How one can reproduce the issue - this is very important. -->
### What is the current *bug* behavior?
<!-- What actually happens. -->
### What is the expected *correct* behavior?
<!-- What you should see instead. -->
### Relevant logs and/or screenshots
<!-- Paste any relevant logs - please use code blocks (```) to format console output,
logs, and code as it's tough to read otherwise.. -->
### Possible fixes
<!-- If you can, link to the line of code that might be responsible for the problem. -->
~bug
### Problem to solve
<!-- What problem do we solve? -->
### Intended users
<!-- Who will use this feature? . It's okay to write "Unknown" and fill this field in later. -->
### Further details
<!-- Include use cases, benefits, and/or goals -->
### Proposal
<!-- How are we going to solve the problem? -->
### Documentation
<!-- Add all known Documentation Requirements here. -->
~feature
\ No newline at end of file
syntax: glob
*~
.spyderworkspace
.spyderproject
.cproject
*.pyc
*.pyd
*.so
*.os
output*
build*
\ No newline at end of file
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: trailing-whitespace
files: ^src/
- id: end-of-file-fixer
files: ^src/
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/pycqa/flake8
rev: 7.0.0
hooks:
- id: flake8
exclude: ^prototypes/
- repo: https://github.com/regebro/pyroma
rev: "4.2"
hooks:
- id: pyroma
\ No newline at end of file
This diff is collapsed.
include src/empyre/vis/mplstyles/*.mplstyle
graft tests
|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
Source diff could not be displayed: it is too large. Options to address this: view the blob.
# -*- 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