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 (63)
Showing
with 1789 additions and 17 deletions
......@@ -11,3 +11,5 @@ 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:
- conda env create -q # -q: quiet/ no progressbar, because it spams the log!
- conda init bash # needed from conda 4.4 onwards, see https://stackoverflow.com/a/55507956/2286972
- source ~/.bashrc # Reldad bashrc to apply changes made by conda init
- 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
......@@ -14,18 +23,19 @@ stages:
test_style:
stage: test
image: continuumio/miniconda3:latest
image: condaforge/miniforge3:latest
script:
# -m: only run tests marked with "flake8"
- pyroma . --min=10 # Checks setup.py for cheese! Maxium cheese 10!
- python setup.py test --addopts "--flake8 -m flake8"
- pip install pre-commit
- pre-commit install
- pre-commit run --all-files
test_function:
stage: test
image: continuumio/miniconda3:latest
image: condaforge/miniforge3:latest
script:
# Execute all tests and also check coverage with --cov:
- python setup.py test --addopts "--cov"
- pip install "numpy>=2"
- pytest --cov
artifacts:
paths:
- .coverage
......@@ -33,16 +43,16 @@ test_function:
test_docs:
stage: test
image: continuumio/miniconda3:latest
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?
# - 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
- sphinx-build docs build/sphinx -W # -w fails the build if warnings appear!
artifacts:
paths:
- build/sphinx
......@@ -50,9 +60,10 @@ test_docs:
test_install:
stage: test
image: continuumio/miniconda3:latest
image: condaforge/miniforge3:latest
before_script: [] # before_script not needed here!
script:
- conda install -y "python<3.12"
- pip install .[all]
pages:
......@@ -67,15 +78,15 @@ pages:
paths:
- public
rules:
- if: '$CI_COMMIT_BRANCH == "master" || $CI_COMMIT_TAG =~ /^\d+\.\d+(\.\d+)?$/'
- if: '$CI_COMMIT_BRANCH == "master" || $CI_COMMIT_TAG =~ /^\d+\.\d+(\.\d+)?$/'
pypi:
stage: deploy
image: continuumio/miniconda3:latest
image: condaforge/miniforge3:latest
before_script: [] # before_script not needed here!
script:
- pip install twine
- python setup.py sdist bdist_wheel
- 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!
......
# 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
include src/empyre/vis/mplstyles/*.mplstyle
graft tests
......@@ -40,6 +40,8 @@ Per default, only the strictly required libraries are installed, but there are a
* ``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.
......
[
{
"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.
# -*- 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
demos/files/confidence.png

1.56 KiB

File deleted
demos/images/activate.png

32.4 KiB

demos/images/addsource.png

56 KiB

demos/images/environment.png

57.1 KiB

demos/images/jutil-gitlab.png

63.1 KiB

demos/images/phasemap-attributes.png

34.6 KiB